Documentation menu
Getting started
QuickstartEndpoints
GET/sourcesGET/tender/alerts/unsubscribeGET/vendorsGET/departmentsGET/expiringGET/tender/feedGET/tender/{record_id}/documents/{doc_index}GET/department/{name}GET/process/{process_key}GET/vendor/{name}GET/contractGET/tenderGET/awardGET/disclosureGET/standing_offerGET/pre_solicitationGET/contract/statsGET/tender/statsGET/award/statsGET/disclosure/statsGET/standing_offer/statsGET/pre_solicitation/statsGET/contract/coverageGET/tender/coverageGET/award/coverageGET/disclosure/coverageGET/standing_offer/coverageGET/pre_solicitation/coverageGET/procurement/{solicitation_number}GET/contract/{record_id}GET/tender/{record_id}GET/award/{record_id}GET/disclosure/{record_id}GET/standing_offer/{record_id}GET/pre_solicitation/{record_id}Errors and troubleshooting
What each status code means, and how to read the message the API sends back.
Every ProcureData error returns the same shape, so you can handle them all the same way:
{
"detail": "limit must be at most 500 (got 1000). Use offset, or cursor for deep pagination, to retrieve more."
}The detail string names the parameter at fault and, where there is one, the nearest valid value. Log it: it is written to be read by a person.
Status codes
| Status | When |
|---|---|
400 | The request is understood but cannot be served as asked, e.g. offset above 9500, or cursor combined with sort_by=value. The message names the fix. |
403 | Missing or invalid API key. |
404 | No such endpoint, or no record with that id. |
422 | A parameter is invalid: an unknown city slug, an impossible date, a limit above 500, an unrecognised sort field, or a misspelled parameter name. The message names the parameter and, where there is one, the nearest valid value. |
429 | Rate limit or plan quota exceeded. |
500 | Unexpected server error. |
504 | The query took too long. Narrow it with a municipality or a date range. |
Common mistakes
- An unknown city slug returns 422 naming the nearest real slug. Call
/{entity_type}/coveragefor every slug in a dataset. - A misspelled parameter returns 422 with a suggestion. Parameter names that are not close to a real one are ignored, so a filter you invented will silently do nothing.
- An empty
resultsarray is not an error. It means the filters matched nothing. Widen the date range or drop a filter to tell the difference between a bad query and a quiet week. - 504 means the query was too broad, not that the API is down. Add a
municipalityor a date range and retry. - On the free tier, the newest 30 days are not there. Responses carry a
free_tierobject saying so, and a record inside the window returns 403 rather than 404 when you ask for it by id. Everything older is included in full.
Rate limits
Every response to a keyed request carries your quota, so you never have to guess how close you are:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed per UTC day on your plan. |
X-RateLimit-Remaining | Requests left today. |
X-RateLimit-Reset | Seconds until the quota resets, which is midnight UTC. |
Past the limit the API answers 429 and adds Retry-After, the same number of seconds, so a client that backs off on that header waits exactly as long as it needs to rather than retrying into a wall. Nothing is charged for a refused request and the counter still rolls at midnight UTC.
Reading X-RateLimit-Remaining is cheaper than handling the 429: a job that pages through results can stop itself and resume tomorrow instead of failing halfway.