Skip to main content

Command Palette

Search for a command to run...

Complete Guide to ERPNext APIs: Usage and Creation

Updated
5 min read
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:

  1. Go to User List

  2. Open your user record

  3. Click on "API Access" section

  4. 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!