Complete Guide to ERPNext APIs: Usage and Creation

ERPNext provides a powerful REST API that allows you to integrate external applications, automate workflows, and extend functionality. This guide will walk you through both consuming existing APIs and creating your own custom APIs in ERPNext.
Understanding ERPNext APIs
ERPNext follows a RESTful architecture, making it easy to interact with your ERP system programmatically. The API supports standard HTTP methods (GET, POST, PUT, DELETE) and returns data in JSON format.
Authentication
Before using any API, you need to authenticate. ERPNext supports two main authentication methods:
Token-Based Authentication
Generate an API key and secret from User settings:
Go to User List
Open your user record
Click on "API Access" section
Generate Keys
Use these credentials in your API requests:
import requests
url = "https://your-site.erpnext.com/api/resource/Item"
headers = {
"Authorization": "token api_key:api_secret"
}
response = requests.get(url, headers=headers)
Password-Based Authentication
Alternatively, use username and password:
import requests
# Login to get session
login_url = "https://your-site.erpnext.com/api/method/login"
credentials = {
"usr": "your_username",
"pwd": "your_password"
}
session = requests.Session()
session.post(login_url, data=credentials)
# Now use the session for API calls
response = session.get("https://your-site.erpnext.com/api/resource/Item")
Using Existing ERPNext APIs
Fetching Documents (GET)
Retrieve a list of documents:
# Get all items
response = requests.get(
"https://your-site.erpnext.com/api/resource/Item",
headers=headers
)
# Get with filters
response = requests.get(
"https://your-site.erpnext.com/api/resource/Item",
headers=headers,
params={
"fields": '["name", "item_name", "item_group"]',
"filters": '[["Item", "item_group", "=", "Products"]]',
"limit_page_length": 20
}
)
Retrieve a specific document:
item_name = "ITEM-001"
response = requests.get(
f"https://your-site.erpnext.com/api/resource/Item/{item_name}",
headers=headers
)
Creating Documents (POST)
# Create a new customer
customer_data = {
"doctype": "Customer",
"customer_name": "John Doe",
"customer_type": "Individual",
"customer_group": "Individual",
"territory": "All Territories"
}
response = requests.post(
"https://your-site.erpnext.com/api/resource/Customer",
headers=headers,
json=customer_data
)
Updating Documents (PUT)
# Update customer
customer_name = "CUST-001"
update_data = {
"mobile_no": "+1234567890",
"email_id": "john@example.com"
}
response = requests.put(
f"https://your-site.erpnext.com/api/resource/Customer/{customer_name}",
headers=headers,
json=update_data
)
Deleting Documents (DELETE)
# Delete a document
response = requests.delete(
f"https://your-site.erpnext.com/api/resource/Customer/{customer_name}",
headers=headers
)
Creating Custom APIs in ERPNext
Now let's create our own custom APIs. There are two main approaches:
Method 1: Whitelisted Functions
Create a Python file in your custom app. For example, in your_app/your_app/api.py:
import frappe
@frappe.whitelist()
def get_customer_orders(customer_name):
"""Get all orders for a specific customer"""
orders = frappe.get_all(
"Sales Order",
filters={"customer": customer_name},
fields=["name", "transaction_date", "grand_total", "status"]
)
return orders
@frappe.whitelist()
def create_custom_item(item_name, item_group, price):
"""Create a new item with custom logic"""
# Validate inputs
if not item_name or not item_group:
frappe.throw("Item name and group are required")
# Create item
item = frappe.get_doc({
"doctype": "Item",
"item_code": item_name,
"item_name": item_name,
"item_group": item_group,
"stock_uom": "Nos"
})
item.insert()
# Create item price if provided
if price:
item_price = frappe.get_doc({
"doctype": "Item Price",
"item_code": item_name,
"price_list": "Standard Selling",
"price_list_rate": price
})
item_price.insert()
frappe.db.commit()
return {
"success": True,
"item_name": item_name,
"message": "Item created successfully"
}
@frappe.whitelist(allow_guest=True)
def public_api_endpoint():
"""This endpoint can be accessed without authentication"""
return {"message": "This is a public endpoint"}
Call these APIs:
# With authentication
response = requests.post(
"https://your-site.erpnext.com/api/method/your_app.api.get_customer_orders",
headers=headers,
json={"customer_name": "CUST-001"}
)
# Public endpoint (no auth needed)
response = requests.get(
"https://your-site.erpnext.com/api/method/your_app.api.public_api_endpoint"
)
Method 2: API Endpoints with Custom Routes
For more control, create custom API endpoints in hooks.py:
# In your_app/hooks.py
app_name = "your_app"
# Add custom API routes
web_api = {
"/api/v1/customers": "your_app.api.v1.customers.get_customers",
"/api/v1/orders": "your_app.api.v1.orders.create_order"
}
Then create the corresponding handlers:
# In your_app/api/v1/customers.py
import frappe
from frappe import _
@frappe.whitelist(allow_guest=False)
def get_customers():
"""Custom endpoint for getting customers with additional logic"""
method = frappe.request.method
if method == "GET":
# Handle GET request
filters = {}
if frappe.request.args.get("territory"):
filters["territory"] = frappe.request.args.get("territory")
customers = frappe.get_all(
"Customer",
filters=filters,
fields=["name", "customer_name", "territory", "customer_group"]
)
return {
"success": True,
"data": customers
}
elif method == "POST":
# Handle POST request
data = frappe.request.json
customer = frappe.get_doc({
"doctype": "Customer",
"customer_name": data.get("customer_name"),
"customer_type": data.get("customer_type", "Company"),
"customer_group": data.get("customer_group", "Commercial"),
"territory": data.get("territory", "All Territories")
})
customer.insert()
frappe.db.commit()
return {
"success": True,
"customer_id": customer.name
}
Best Practices
Error Handling
Always implement proper error handling:
import frappe
@frappe.whitelist()
def safe_api_method(item_code):
try:
# Check if item exists
if not frappe.db.exists("Item", item_code):
frappe.throw(_("Item {0} does not exist").format(item_code))
item = frappe.get_doc("Item", item_code)
return {
"success": True,
"data": item.as_dict()
}
except frappe.DoesNotExistError:
frappe.response["http_status_code"] = 404
return {
"success": False,
"error": "Item not found"
}
except Exception as e:
frappe.log_error(frappe.get_traceback(), "API Error")
frappe.response["http_status_code"] = 500
return {
"success": False,
"error": str(e)
}
Permission Checks
Always validate permissions:
@frappe.whitelist()
def update_sensitive_data(doctype, name, field, value):
# Check if user has permission
if not frappe.has_permission(doctype, "write", name):
frappe.throw(_("You don't have permission to update this document"))
doc = frappe.get_doc(doctype, name)
doc.set(field, value)
doc.save()
return {"success": True}
Input Validation
Validate all inputs:
@frappe.whitelist()
def create_sales_order(customer, items):
# Validate customer
if not frappe.db.exists("Customer", customer):
frappe.throw(_("Invalid customer"))
# Validate items
if not items or not isinstance(items, list):
frappe.throw(_("Items are required and must be a list"))
# Process the order
# ...
Testing Your APIs
Use tools like Postman or cURL to test:
# GET request
curl -X GET \
'https://your-site.erpnext.com/api/resource/Item' \
-H 'Authorization: token api_key:api_secret'
# POST request
curl -X POST \
'https://your-site.erpnext.com/api/method/your_app.api.create_custom_item' \
-H 'Authorization: token api_key:api_secret' \
-H 'Content-Type: application/json' \
-d '{
"item_name": "New Product",
"item_group": "Products",
"price": 100
}'
Conclusion
ERPNext's API system is flexible and powerful, allowing you to build robust integrations and custom functionality. Start with the built-in REST API for standard operations, and create custom whitelisted functions when you need specialized logic. Always remember to implement proper authentication, error handling, and permission checks to keep your API secure and reliable.
Happy coding!



