Errors & Limits
Standard HTTP status codes, async job envelopes, and per-key limits help you build reliable integrations.
AgentSEO uses normal HTTP status codes for request-level failures and a structured job status envelope for async work. Rate limits are split between workload traffic and higher-budget control traffic such as job polling and SSE streams.
HTTP Status Codes
| Code | Meaning | Description |
|---|---|---|
| 200 | OK | Request succeeded. Result is in the body. |
| 202 | Accepted | Job queued or still processing. Poll /jobs/{id} or stream /jobs/{id}/events. |
| 400 | Bad Request | Invalid parameters. check details field. |
| 401 | Unauthorized | Invalid or missing API key. |
| 402 | Payment Required | Credits are exhausted for the API key or workspace budget path in use. |
| 403 | Forbidden | Key is revoked, expired, or blocked by an allowed-domain policy. |
| 404 | Not Found | Resource or endpoint does not exist. |
| 429 | Too Many Requests | Rate limit exceeded. Read RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset to decide when to retry. |
| 5xx | Server Error | Something went wrong on our end or in an upstream provider path. Retry transient failures and inspect job-level error codes on async workflows. |
Error Response Format
{
"error": {
"code": "INVALID_PARAMETERS",
"message": "The request parameters are invalid.",
"details": {
"domain": {
"_errors": ["Domain or Business Name is required"]
}
},
"request_id": "req_123xyz"
}
}{
"error": {
"code": "CREDITS_EXHAUSTED",
"message": "Insufficient credits to run this request.",
"details": {
"credits_remaining": 0
},
"request_id": "req_456abc"
}
}Async Job Status Format
Most workflow endpoints are async-first. A queued or in-flight job can be polled through /jobs/{id} or streamed through /jobs/{id}/events. Completed jobs return result; failed jobs return a structured error block with a retryable flag.
{
"jobId": "123e4567-e89b-12d3-a456-426614174000",
"status": "processing",
"phase": "waiting_upstream",
"progress": {
"percent": 55,
"indeterminate": true,
"eta_seconds": 12
},
"attempt": 1,
"max_attempts": 3,
"message": "Waiting for upstream provider results.",
"poll_url": "/api/v1/jobs/123e4567-e89b-12d3-a456-426614174000",
"events_url": "/api/v1/jobs/123e4567-e89b-12d3-a456-426614174000/events",
"retry_after_seconds": 2,
"upstream": {
"provider": "dataforseo",
"task_id": "0228...",
"status_code": 40602,
"status_message": "Task In Queue.",
"next_poll_at": "2026-02-28T12:00:10.000Z"
}
}{
"jobId": "123e4567-e89b-12d3-a456-426614174000",
"status": "failed",
"phase": "finalizing",
"progress": {
"percent": 100,
"indeterminate": false,
"eta_seconds": null
},
"attempt": 3,
"max_attempts": 3,
"message": "Job failed.",
"error": {
"code": "UPSTREAM_FETCH_FAILED",
"message": "UPSTREAM_FETCH_FAILED: fetch failed",
"retryable": true
},
"poll_url": "/api/v1/jobs/123e4567-e89b-12d3-a456-426614174000",
"events_url": "/api/v1/jobs/123e4567-e89b-12d3-a456-426614174000/events",
"retry_after_seconds": 2
}Rate Limits
Limits are applied per API key. AgentSEO uses a lower budget for workload requests that create or run SEO work, and a higher budget for control-plane requests such as /jobs/{id} polling and /jobs/{id}/events SSE connections.
| Plan | Workload | Control |
|---|---|---|
| Hobby | 60 / minute | 600 / minute |
| Starter | 120 / minute | 1200 / minute |
| Pro | 300 / minute | 3000 / minute |
| Agency | 1000 / minute | 10000 / minute |
On rate-limit responses, read RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset. Retry-After is used on some sync-pending job responses, not as the main rate-limit signal.