Errors
HTTP status codes, the error body, error codes and rate limits
Errors
The Jobiflow API uses conventional HTTP status codes. Every error on a /public/v1/ endpoint answers with the same JSON body, ErrorDetails.
HTTP Status Codes
| Code | Meaning |
|---|---|
200 OK | Request succeeded |
201 Created | Resource created |
204 No Content | Request succeeded, no body (deletes) |
400 Bad Request | The body or a parameter is invalid |
401 Unauthorized | Missing, unknown, revoked or expired API key, or an invalid bearer header next to it |
402 Payment Required | Publishing a listing: the company's credits do not suffice |
403 Forbidden | The key lacks the endpoint's scope |
404 Not Found | The resource does not exist or belongs to another company, or no endpoint has this path |
405 Method Not Allowed | The path exists, but not with this method; the Allow header lists the methods it takes |
409 Conflict | External application: the candidate already applied to this listing. Deleting a listing that has applications |
415 Unsupported Media Type | The body is not sent as application/json |
429 Too Many Requests | The key's rate limit is used up |
500 Internal Server Error | Unexpected failure in the API |
502 Bad Gateway | A service behind the API failed |
503 Service Unavailable | A service behind the API did not answer |
Error Body
{
"errorCode": "INVALID_REQUEST",
"message": "Invalid request",
"statusCode": 400,
"path": "/public/v1/job-listings",
"timestamp": "2026-10-25T12:00:00.123",
"fieldErrors": [
{ "field": "position", "message": "must not be null" }
]
}| Field | Description |
|---|---|
errorCode | Machine-readable error code (see below); branch on this |
message | English description, for logs |
statusCode | The HTTP status code |
path | The request path |
timestamp | When the error happened, ISO 8601 without offset |
fieldErrors | Only on validation errors: the fields that failed, each with field and message |
Error Codes
| Code | Status | Description |
|---|---|---|
INVALID_REQUEST | 400 | The body or a parameter is invalid; fieldErrors lists the fields on a validation error |
INVALID_REQUEST | 404, 405, 415 | The request matches no endpoint: unknown path (404), a method the path does not take (405), a body that is not JSON (415) |
INVALID_API_KEY | 401 | No X-API-Key header, or the key is unknown, revoked or expired |
TOKEN_INVALID | 401 | A valid key was sent with an invalid Authorization: Bearer header |
INSUFFICIENT_CREDITS | 402 | Publishing needs more credits than the company has |
ACCESS_DENIED | 403 | The key lacks the endpoint's scope |
NOT_FOUND | 404 | The listing, application or other resource does not exist or belongs to another company |
JOBSEEKER_NOT_FOUND | 404 | The candidate does not exist or is not visible to your company |
WEBHOOK_SUBSCRIPTION_NOT_FOUND | 404 | The webhook subscription does not exist or belongs to another company |
JOBSEEKER_ALREADY_APPLIED | 409 | External application: the candidate already applied to this listing |
JOBLISTING_HAS_APPLICATIONS | 409 | The listing has applications, so it cannot be deleted; archive it instead (PATCH /public/v1/job-listings/{id}/archive) |
RATE_LIMIT_EXCEEDED | 429 | The key's rate limit is used up; wait for Retry-After |
UNEXPECTED_ERROR | 500, 502, 503 | A failure in the API or in a service behind it; retry later |
A 400 that a service behind the API raises keeps that service's code, for example INVALID_JOBLISTING_STATUS_TRANSITION for a status change the listing cannot make.
A 404 therefore carries NOT_FOUND (or a resource's own code) when the endpoint exists but the resource does not, and INVALID_REQUEST when no endpoint has the path. The key is checked first: without a valid key every one of these answers 401 INVALID_API_KEY.
Handling Errors
const res = await fetch('https://api.jobiflow.com/public/v1/job-listings', {
headers: { 'X-API-Key': process.env.JOBIFLOW_API_KEY },
});
if (!res.ok) {
const err = await res.json();
console.error(`[${err.errorCode}] ${err.message}`);
for (const field of err.fieldErrors ?? []) {
console.error(` - ${field.field}: ${field.message}`);
}
throw new Error(err.errorCode);
}
const { data, pagination } = await res.json();Rate Limits
Each API key may send 60 requests per minute and 10 per second. Beyond that the API answers 429 Too Many Requests with RATE_LIMIT_EXCEEDED and a Retry-After header: the number of seconds to wait.
HTTP/1.1 429 Too Many Requests
Retry-After: 1