API Fundamentals
Modern REST API Reference and Best Practices
A comprehensive, developer-friendly guide to understanding and implementing RESTful APIs. This documentation covers authentication, endpoint design, error handling, and advanced features for building robust web services.\
Quickstart
Get started with the API in minutes. All requests use HTTPS and communicate with JSON payloads. The API follows RESTful conventions with standard HTTP methods and status codes.
Making Your First Request
curl -s \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
https://api.example.com/v1/items?limit=25Response Format
All successful responses return JSON with a predictable structure:
{
"data": [...],
"meta": {
"count": 25,
"next_cursor": "eyJvZmZzZXQiOjI1fQ"
}
}Authentication
The API uses Bearer token authentication for secure access. Every request must include a valid API token in the Authorization header.
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Client │ │ API Gateway │ │ API Server │
│ │ │ │ │ │
│ + Token │─────────▶│ Validates │─────────▶ │ Processes │
│ │ │ Token │ │ Request │
└─────────────┘ └──────────────┘ └─────────────┘
↑ │
│ │
└───────────────────────────┘
Returns ResponseObtaining API Tokens
- Log into your account dashboard
- Navigate to Settings → API Keys
- Generate a new token with appropriate scopes
- Store your token securely (never commit to version control)
Authorization Header
Include your token in every request:
# Using curl
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
https://api.example.com/v1/itemsToken Scopes
Tokens support granular permissions to control access levels:
Security Best Practice: Always use the minimum required scope for your application. Rotate tokens regularly and revoke unused tokens immediately.
Endpoints
The API provides CRUD operations for managing items through intuitive REST endpoints. All endpoints accept and return JSON unless otherwise specified.
HTTP Method Endpoint Action
───────────────────────────────────────────────────────
GET /items List all items
POST /items Create new item
GET /items/\{id\} Get single item
PATCH /items/\{id\} Update item
DELETE /items/\{id\} Delete itemGET /items
List Items
Retrieve a paginated list of all items accessible to your account. Supports filtering, sorting, and cursor-based pagination for efficient data retrieval.
Query Parameters
Request Example
curl -X GET "https://api.example.com/v1/items?limit=10&sort=created_at&order=desc" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json"Response Example
{
"data": [
{
"id": "it_123",
"name": "Alpha Product",
"status": "active",
"created_at": "2024-12-01T10:30:00Z",
"updated_at": "2024-12-15T14:22:00Z"
},
{
"id": "it_124",
"name": "Beta Service",
"status": "active",
"created_at": "2024-12-05T08:15:00Z",
"updated_at": "2024-12-10T16:45:00Z"
}
],
"meta": {
"count": 2,
"next_cursor": "eyJvZmZzZXQiOjEwfQ"
}
}Response Status Codes
200 OK- Items retrieved successfully400 Bad Request- Invalid query parameters401 Unauthorized- Missing or invalid authentication429 Too Many Requests- Rate limit exceeded
POST /items
Create New Item
Create a new item with specified attributes. Returns the created item with generated ID and timestamps.
Required Scope
items:write
Request Body
Request Example
curl -X POST "https://api.example.com/v1/items" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "New Product Launch",
"description": "Q1 2025 flagship product",
"status": "draft",
"metadata": {
"category": "electronics",
"priority": "high"
}
}'Response Example
{
"data": {
"id": "it_125",
"name": "New Product Launch",
"description": "Q1 2025 flagship product",
"status": "draft",
"metadata": {
"category": "electronics",
"priority": "high"
},
"created_at": "2024-12-17T09:00:00Z",
"updated_at": "2024-12-17T09:00:00Z"
}
}Response Status Codes
201 Created- Item created successfully400 Bad Request- Validation error in request body401 Unauthorized- Missing or invalid authentication403 Forbidden- Insufficient permissions (missingitems:write)422 Unprocessable Entity- Semantic validation error
GET /items/{id}
Retrieve Single Item
Fetch detailed information for a specific item by its unique identifier.
Path Parameters
Request Example
curl -X GET "https://api.example.com/v1/items/it_123" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json"Response Example
{
"data": {
"id": "it_123",
"name": "Alpha Product",
"description": "Premium product line",
"status": "active",
"metadata": {
"category": "electronics",
"sku": "APH-001"
},
"created_at": "2024-12-01T10:30:00Z",
"updated_at": "2024-12-15T14:22:00Z"
}
}Response Status Codes
200 OK- Item retrieved successfully401 Unauthorized- Missing or invalid authentication404 Not Found- Item does not exist or is inaccessible
PATCH /items/{id}
Update Item
Partially update an existing item. Only provided fields will be modified; omitted fields remain unchanged.
Required Scope
items:write
Path Parameters
Request Body
Any combination of updateable fields from the create endpoint. All fields are optional.
Request Example
curl -X PATCH "https://api.example.com/v1/items/it_123" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "active",
"metadata": {
"featured": true
}
}'Response Example
{
"data": {
"id": "it_123",
"name": "Alpha Product",
"description": "Premium product line",
"status": "active",
"metadata": {
"category": "electronics",
"sku": "APH-001",
"featured": true
},
"created_at": "2024-12-01T10:30:00Z",
"updated_at": "2024-12-17T09:15:00Z"
}
}Response Status Codes
200 OK- Item updated successfully400 Bad Request- Invalid update data401 Unauthorized- Missing or invalid authentication403 Forbidden- Insufficient permissions404 Not Found- Item does not exist422 Unprocessable Entity- Validation error
DELETE /items/{id}
Delete Item
Permanently remove an item from your account. This action cannot be undone.
Required Scope
items:write
Path Parameters
Request Example
curl -X DELETE "https://api.example.com/v1/items/it_123" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json"Response Example
{
"data": {
"id": "it_123",
"deleted": true,
"deleted_at": "2024-12-17T09:30:00Z"
}
}Response Status Codes
200 OK- Item deleted successfully401 Unauthorized- Missing or invalid authentication403 Forbidden- Insufficient permissions404 Not Found- Item does not exist or already deleted
Pagination
The API uses cursor-based pagination for efficient navigation through large datasets. This approach provides consistent results even when data changes between requests.
Request 1 Request 2 Request 3
──────────────────────────────────────────────────────────────
?limit=25 → ?limit=25 → ?limit=25
&cursor=ABC123 &cursor=XYZ789
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Items 1-25 │ │ Items 26-50│ │ Items 51-75│
│ │ │ │ │ │
│ cursor: │ │ cursor: │ │ cursor: │
│ ABC123 │ │ XYZ789 │ │ null │
└────────────┘ └────────────┘ └────────────┘How Cursor Pagination Works
Instead of page numbers or offsets, each response includes an opaque next_cursor token that points to the next set of results. Cursors are time-limited and typically valid for 24 hours.
Pagination Parameters
Example Workflow
Initial Request:
curl "https://api.example.com/v1/items?limit=25"Response with Cursor:
{
"data": [...],
"meta": {
"count": 25,
"next_cursor": "eyJvZmZzZXQiOjI1fQ",
"has_more": true
}
}Next Page Request:
curl "https://api.example.com/v1/items?limit=25&cursor=eyJvZmZzZXQiOjI1fQ"Best Practices
- Always check the
has_morefield to determine if additional pages exist - Store cursors temporarily; they expire after 24 hours
- Use consistent
limitvalues throughout pagination - Never attempt to manually construct or decode cursor tokens
Filtering & Sorting
Refine query results using flexible filtering and multi-field sorting capabilities.
Filtering
Apply filters using query parameters that correspond to item fields:
curl "https://api.example.com/v1/items?status=active&q=electronics" \
-H "Authorization: Bearer YOUR_API_TOKEN"Available Filters
Sorting
Sort results by any supported field using sort and order parameters:
curl "https://api.example.com/v1/items?sort=created_at&order=desc" \
-H "Authorization: Bearer YOUR_API_TOKEN"Sortable Fields
name- Alphabetical by namecreated_at- Creation timestampupdated_at- Last modification timestampstatus- Status value (alphabetical)
Complex Query Example
curl "https://api.example.com/v1/items?status=active&sort=updated_at&order=desc&limit=50&q=premium" \
-H "Authorization: Bearer YOUR_API_TOKEN"This retrieves the 50 most recently updated active items matching “premium”, sorted newest first.
Rate Limits
To ensure fair usage and system stability, the API enforces rate limits on all requests. Limits are applied per API token.
Rate Limit Window (1 minute)
────────────────────────────────────────────────────
0s 30s 60s
├──────────────────────┼──────────────────────┤
│ ████████████████████ │ ░░░░░░░░░░░░░░░░░░░░ │
│ 60 requests used │ 0 requests left │
└──────────────────────┴──────────────────────┘
Reset at 60sRate Limit Tiers
Rate Limit Headers
Every response includes headers showing your current rate limit status:
# Example response headers
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1702814400Handling Rate Limits
When you exceed your rate limit, the API returns a 429 Too Many Requests response:
{
"error": {
"type": "rate_limit_exceeded",
"message": "Rate limit exceeded. Retry after 45 seconds.",
"retry_after": 45
}
}Best Practices
- Monitor
X-RateLimit-Remainingheader values - Implement exponential backoff when receiving 429 responses
- Cache frequently accessed data to reduce API calls
- Use webhooks instead of polling for real-time updates
- Consider upgrading your plan if consistently hitting limits
Errors
The API uses conventional HTTP status codes and returns detailed error objects to help diagnose issues quickly.
Error Flow
──────────────────────────────────────────────────────
Client Request
│
▼
┌─────────────┐
│ Validation │──── Pass ────▶ Process Request
│ │
└─────────────┘
│ Fail
▼
┌─────────────┐
│ Error │
│ Response │
│ + Details │
└─────────────┘HTTP Status Code Summary
Error Response Format
All errors return a consistent JSON structure:
{
"error": {
"type": "validation_error",
"message": "The provided data is invalid.",
"code": "INVALID_REQUEST",
"details": [
{
"field": "name",
"message": "Name must be between 3 and 100 characters.",
"code": "length_out_of_range"
}
],
"request_id": "req_abc123xyz"
}
}Error Object Fields
Common Error Types
validation_error - Request data failed validation
{
"error": {
"type": "validation_error",
"message": "Invalid request parameters.",
"code": "VALIDATION_FAILED"
}
}authentication_error - Authentication credentials invalid
{
"error": {
"type": "authentication_error",
"message": "Invalid or expired API token.",
"code": "INVALID_TOKEN"
}
}resource_not_found - Requested resource doesn’t exist
{
"error": {
"type": "resource_not_found",
"message": "The requested item could not be found.",
"code": "NOT_FOUND"
}
}rate_limit_exceeded - Too many requests
{
"error": {
"type": "rate_limit_exceeded",
"message": "Rate limit exceeded. Retry after 60 seconds.",
"code": "RATE_LIMIT",
"retry_after": 60
}
}Error Handling Best Practices
- Always check HTTP status codes before parsing response bodies
- Log
request_idvalues when contacting support - Parse
detailsarrays for field-specific validation errors - Implement retry logic with exponential backoff for 5xx errors
- Never expose raw error messages to end users; provide user-friendly alternatives
Webhooks
Receive real-time notifications when events occur in your account. Webhooks eliminate the need for polling and enable event-driven architectures.
Webhook Flow
────────────────────────────────────────────────────
Event Occurs
│
▼
┌─────────────┐
│ API Server │
│ │
│ Triggers │
│ Webhook │
└─────────────┘
│
▼
┌─────────────┐ ┌─────────────┐
│ POST │─────────▶│ Your Server │
│ Request │ │ │
│ │◀─────────│ 200 OK │
└─────────────┘ └─────────────┘How Webhooks Work
When a subscribed event occurs, the API sends an HTTP POST request to your configured webhook URL with event details in the request body.
Configuring Webhooks
- Navigate to Settings → Webhooks in your dashboard
- Click “Add Webhook Endpoint”
- Enter your HTTPS endpoint URL
- Select events to subscribe to
- Save and note your webhook signing secret
Webhook Events
Webhook Payload Format
{
"id": "evt_abc123",
"type": "item.created",
"created_at": "2024-12-17T10:00:00Z",
"data": {
"id": "it_126",
"name": "New Item",
"status": "active",
"created_at": "2024-12-17T10:00:00Z"
}
}Verifying Webhook Signatures
Every webhook request includes an X-Webhook-Signature header containing an HMAC-SHA256 signature. Always verify this signature to ensure the request originated from our API.
Verification Example (Node.js):
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}Webhook Best Practices
- Respond with
200 OKwithin 5 seconds to acknowledge receipt - Process events asynchronously to avoid timeouts
- Implement idempotency using the
idfield to handle duplicate deliveries - Store webhook secrets securely (environment variables, secret managers)
- Return 2xx status codes only after successful processing
- Failed webhooks are retried up to 3 times with exponential backoff
SDK Integration
Get started quickly with our lightweight SDK for popular programming languages.
JavaScript / Node.js
const API_BASE = 'https://api.example.com/v1';
const TOKEN = 'YOUR_API_TOKEN';
const api = {
async request(endpoint, options = {}) {
const res = await fetch(`$\{API_BASE\}$\{endpoint\}`, {
...options,
headers: {
'Authorization': `Bearer $\{TOKEN\}`,
'Content-Type': 'application/json',
...options.headers
}
});
if (!res.ok) throw new Error(await res.text());
return res.json();
},
get: (endpoint) => api.request(endpoint),
post: (endpoint, data) => api.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
}),
patch: (endpoint, data) => api.request(endpoint, {
method: 'PATCH',
body: JSON.stringify(data)
}),
delete: (endpoint) => api.request(endpoint, { method: 'DELETE' })
};
// Usage
const items = await api.get('/items?limit=10');
const newItem = await api.post('/items', { name: 'Test', status: 'active' });
await api.patch(`/items/${newItem.data.id}`, { status: 'archived' });
await api.delete(`/items/${newItem.data.id}`);Python
import requests
class API:
def __init__(self, token):
self.base = 'https://api.example.com/v1'
self.headers = {
'Authorization': f'Bearer \{token\}',
'Content-Type': 'application/json'
}
def get(self, path, params=None):
return requests.get(f'{self.base}\{path\}',
headers=self.headers, params=params).json()
def post(self, path, data):
return requests.post(f'{self.base}\{path\}',
headers=self.headers, json=data).json()
def patch(self, path, data):
return requests.patch(f'{self.base}\{path\}',
headers=self.headers, json=data).json()
def delete(self, path):
return requests.delete(f'{self.base}\{path\}',
headers=self.headers).json()
# Usage
api = API('YOUR_API_TOKEN')
items = api.get('/items', {'limit': 10})
new_item = api.post('/items', {'name': 'Test', 'status': 'active'})
api.patch(f'/items/{new_item["data"]["id"]}', {'status': 'archived'})
api.delete(f'/items/{new_item["data"]["id"]}')PHP
<?php
class API {
private $base = 'https://api.example.com/v1';
private $token;
public function __construct($token) {
$this->token = $token;
}
private function request($method, $path, $data = null) {
$ch = curl_init($this->base . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->token,
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => $data ? json_encode($data) : null
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
public function get($path, $params = []) {
$query = http_build_query($params);
return $this->request('GET', $path . ($query ? '?' . $query : ''));
}
public function post($path, $data) {
return $this->request('POST', $path, $data);
}
public function patch($path, $data) {
return $this->request('PATCH', $path, $data);
}
public function delete($path) {
return $this->request('DELETE', $path);
}
}
// Usage
$api = new API('YOUR_API_TOKEN');
$items = $api->get('/items', ['limit' => 10]);
$newItem = $api->post('/items', ['name' => 'Test', 'status' => 'active']);
$api->patch('/items/' . $newItem['data']['id'], ['status' => 'archived']);
$api->delete('/items/' . $newItem['data']['id']);
?>Ruby
require 'net/http'
require 'json'
class API
BASE = 'https://api.example.com/v1'
def initialize(token)
@token = token
end
def request(method, path, data = nil)
uri = URI("#\{BASE\}#\{path\}")
req = Net::HTTP.const_get(method).new(uri)
req['Authorization'] = "Bearer #{@token}"
req['Content-Type'] = 'application/json'
req.body = data.to_json if data
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(req)
end
JSON.parse(res.body)
end
def get(path, params = {})
uri = URI("#\{BASE\}#\{path\}")
uri.query = URI.encode_www_form(params) unless params.empty?
request('Get', uri.request_uri)
end
def post(path, data)
request('Post', path, data)
end
def patch(path, data)
request('Patch', path, data)
end
def delete(path)
request('Delete', path)
end
end
# Usage
api = API.new('YOUR_API_TOKEN')
items = api.get('/items', limit: 10)
new_item = api.post('/items', { name: 'Test', status: 'active' })
api.patch("/items/#{new_item['data']['id']}", { status: 'archived' })
api.delete("/items/#{new_item['data']['id']}")