API Integration Guide
API Best Practices
How to stay under endpoint rate limits, reduce credit usage, and handle errors gracefully when integrating Data Docked.
TL;DR
- •Rate limits are per-endpoint, not per-plan.
- •Limits range from 15 to 100 requests/minute depending on the endpoint.
- •Credit cost is documented on each endpoint’s reference page.
- •Use caching, batching, and exponential backoff to stay under limits.
Rate Limits Explained
Each endpoint has its own request-per-minute ceiling, set based on the cost of the underlying query. Exceeding it returns 429 Too Many Requests.
Heavier endpoints that return larger payloads or run more expensive queries.
Reference and lookup endpoints with moderate payloads.
Realtime position lookups designed for high-frequency polling.
See each endpoint’s reference page for its exact limit and credit cost — and the pricing page for plan options.
Optimize Your Usage
Apply these patterns to keep request volume — and credit burn — proportional to the data you actually need.
Efficient Querying
Use batch endpoints when possible
Instead of making individual requests for each vessel, use our vessels-by-area or vessel-location-bulk endpoints with multiple identifiers.
// Instead of multiple single requests
// GET /api/v1/vessel/211234567
// GET /api/v1/vessel/211234568
// GET /api/v1/vessel/211234569
// Use the bulk endpoint (1 request)
GET /api/v1/vessels?mmsi=211234567,211234568,211234569Filter by bounding box
When monitoring an area, define the smallest bounding box that covers your region of interest to reduce data transfer.
// Singapore Strait focused area
GET /api/v1/vessels/area?lat_min=1.1&lat_max=1.4&lon_min=103.5&lon_max=104.1Use field selection
Request only the fields you need to reduce response size and improve parsing speed.
// Only get position and identity data
GET /api/v1/vessel/211234567?fields=mmsi,name,lat,lon,speed,course,timestampCaching Strategy
Cache static vessel data
Vessel name, IMO, dimensions, and type rarely change. Cache this data for 24 hours and refresh only when needed.
Respect update frequencies
Terrestrial AIS positions refresh every 2-10 seconds; satellite positions every few minutes. Polling faster than the data changes wastes credits and pressures rate limits.
Implement local storage
Store historical tracks locally rather than re-requesting them. Once retrieved, track data doesn't change.
Architecture Patterns
Use a request queue
Implement a queue that respects each endpoint's req/min band and processes requests in order to avoid bursts.
Centralize API calls
Route all API calls through a single service to track usage per endpoint and apply consistent caching.
Consider webhooks for high-volume
For real-time updates on many vessels, webhooks are more efficient than polling. Contact us for enterprise webhook support.
Handle Errors Gracefully
Treat 429 as expected — pace requests to the endpoint’s band rather than reacting after a failure. Distinguish 403 (out of credits) from 401 (bad or missing key).
HTTP Status Codes
| Code | Status | Description | Action |
|---|---|---|---|
| 200 | OK | Request successful | Process response normally |
| 400 | Bad Request | Invalid parameters | Check request format and parameters |
| 401 | Unauthorized | Invalid or missing API key | Verify API key is correct |
| 403 | Forbidden | Out of credits or access denied | Check credit balance — distinct from 401 (auth) |
| 404 | Not Found | Resource not found | Verify MMSI/IMO exists in database |
| 429 | Too Many Requests | Endpoint rate limit exceeded | Pace requests to the band; retry with backoff |
| 500 | Server Error | Internal server error | Retry after delay, contact support if persistent |
Retry with exponential backoff
async function fetchWithRetry(url, maxRetries = 5) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url);
if (response.status === 429) {
const waitTime = Math.pow(2, i) * 1000;
await sleep(waitTime);
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}Monitor Your Usage
Two complementary signals tell you when you’re approaching a limit: the dashboard credit balance and your own client-side request counters.
Dashboard credits widget
Your current credit balance is visible in the account dashboard. Use it to verify usage and top up before you run out — a 403 response means your balance hit zero.
Track 429s in your own logs
Instrument a counter per endpoint on the client side. Alert when request volume approaches the band’s req/min ceiling — and when a 429 actually fires, so you can tune your pacing.
Complete Retry Example
A production-ready client with paced retries and clean error handling.
class DataDockedClient {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.datadocked.com/api/v1';
}
async request(endpoint, options = {}) {
const maxRetries = 3;
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
...options.headers,
},
});
// Rate limited — back off exponentially
if (response.status === 429) {
const waitMs = Math.pow(2, attempt) * 1000;
await this.sleep(waitMs);
continue;
}
// Server error — retry with backoff
if (response.status >= 500) {
const waitMs = Math.pow(2, attempt) * 1000;
await this.sleep(waitMs);
continue;
}
// Success or client error — return as-is
return response.json();
} catch (error) {
lastError = error;
const waitMs = Math.pow(2, attempt) * 1000;
await this.sleep(waitMs);
}
}
throw lastError || new Error('Request failed after retries');
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Get single vessel
async getVessel(mmsi) {
return this.request(`/vessel/${mmsi}`);
}
// Get vessels in area
async getVesselsInArea(bounds) {
const { latMin, latMax, lonMin, lonMax } = bounds;
return this.request(
`/vessels/area?lat_min=${latMin}&lat_max=${latMax}&lon_min=${lonMin}&lon_max=${lonMax}`
);
}
}
// Usage
const client = new DataDockedClient('your_api_key');
const vessel = await client.getVessel('211234567');Ready to Start Building?
Sign up for free and get 20 credits to test our API. No credit card required.