API Overview
Authentication, rate limits, and error handling for the MetricDuck API.
Base URL
https://api.metricduck.com/api/v1All endpoints are relative to this base URL. The API uses HTTPS only.
Authentication
Authenticate REST requests by sending your API key in the X-API-Key header.
curl -X GET "https://api.metricduck.com/api/v1/data/metrics?tickers=AAPL&metrics=revenues&period=quarterly&years=1" \
-H "X-API-Key: fda_your_api_key_here"| Where | How to send your key |
|---|---|
| REST API (api.metricduck.com) | X-API-Key: fda_… |
| MCP server (mcp.metricduck.com/mcp) | OAuth (your client signs you in), or Authorization: Bearer fda_… |
On REST, ?api_key=fda_… also works, for quick browser tests only. Authorization: Bearer on REST is for web sessions, not API keys: a key sent that way isn't recognized, so the request is served as a guest, or refused with 401 on a route that needs a key.
Security Best Practices
- - Never expose API keys in client-side code
- - Store keys in environment variables
- - Use different keys for development and production
- - Rotate keys periodically
fda_- Every API key starts with fda_
Generate API keys from your Dashboard → API Keys
Quotas & Rate Limits
Quotas are counted in tool calls. A tool call is one MCP tool call (internal fan-out is not counted) or one REST request made with your API key. Free has a daily cap that resets at 00:00 UTC. Pro has no daily cap and a monthly allowance.
| Tier | Tool calls | Price |
|---|---|---|
| Free | 500 tool calls/day (resets 00:00 UTC) | $0 |
| Pro | No daily cap · 50,000 tool calls/month | $20/month |
Credits (the bulk /data/metrics endpoint only): each call there is one tool call and also costs credits = tickers × metrics × periods (periods = years, or years × 4 for quarterly; each dimension adds 1). Free: 500 credits per UTC day. Pro: 50,000 credits a month. dry_run=true returns the estimate without running the query.
Rate Limit Headers
Rate-limited routes return per-minute limits in these headers. They are not the daily or monthly quota:
X-RateLimit-Limit- Requests allowed per minuteX-RateLimit-Remaining- Requests left in the current minuteX-RateLimit-Reset- Unix timestamp when the minute window resets
Need higher limits? View pricing plans
Error Handling
The API uses standard HTTP status codes. Error responses include a JSON body with details.
Error Response Format
{
"success": false,
"error": "Invalid or missing authentication credentials. Use cookie, X-API-Key header, or ?api_key= query parameter.",
"request_id": "4fae07b0"
}| Status | Name | Description |
|---|---|---|
400 | Bad Request | Invalid request parameters or malformed request body |
401 | Unauthorized | Missing or invalid API key on a route that requires one. Send the key in the X-API-Key header |
403 | Forbidden | Valid API key but insufficient permissions for this endpoint (for example, a free key on a Pro-only route) |
404 | Not Found | The requested resource (company, endpoint) does not exist |
429 | Too Many Requests | Too many requests: the per-minute rate limit (see the X-RateLimit-* headers), the free daily tool-call cap (error_code FREE_DAILY_CAP, with resets_at and a Retry-After header), or the /data/metrics credit budget |
500 | Internal Server Error | Server error. Retry the request or contact support |
Response Format
Responses are JSON, and the shape depends on the route: each route's page shows its own. Statement and metric routes take a window such as years; list routes page, each as its page documents (for example, /filings/text takes limit and offset). For example, /data/metrics returns:
{
"data": {
"AAPL": {
"company_name": "Apple Inc.",
"cik": "0000320193",
"metrics": {
"revenues": {
"display_name": "Revenues",
"unit": null,
"values": [
{ "period": "...", "period_end": "...", "value": 0, "dimension": null }
]
}
}
}
},
"metadata": { "request": { ... }, "resolved_tickers": 1, "total_values": 4, "credits": { ... } }
}Ready to make your first API call?
Start with the Universe endpoint to discover available companies, or jump straight to financial data.