Errors
HTTP status codes and JSON bodies for the Publishers API.
Two response shapes
Survey endpoint (POST /api/v1/publishers/surveys)
Most responses use:
{
"requestStatus": "true" | "false",
"message": "…"
}Success with offers uses requestStatus: "true". Failures use "false" plus message.
Authentication failures
Missing or invalid API keys return HTTP 401 with a top-level error field (not requestStatus):
{
"error": "Invalid or missing API key. Use Authorization: Bearer <key> or X-API-Key header."
}HTTP status codes
| Status | Meaning | What you should do |
|---|---|---|
| 200 | Request processed | Read requestStatus and hasSurveys |
| 400 | Invalid request body, validation error, or redirect URLs not configured | Fix payload or portal config |
| 401 | Invalid or missing API key | Check authentication |
| 403 | Key valid but account not permitted | Sign in to the portal once and create the key there; otherwise contact support |
| 404 | Unknown appId for your organization | Verify appId in the portal |
| 405 | Wrong HTTP method | Use POST only |
| 429 | Rate limit exceeded for your API key | Wait for Retry-After seconds, then retry (see Rate limits) |
| 502 | Survey matching temporarily unavailable | Retry with exponential backoff |
| 504 | Gateway timeout in front of the API (rare) | Retry with exponential backoff |
Common HTTP 400 causes
message (or symptom) | Cause | Fix |
|---|---|---|
App redirect URLs … must all be configured | Complete, Screen-out, Cancelled URL or signing secret missing | Fill in all four under Apps → your app → Settings |
Missing required field: panelistId although you sent it | panelistId or appId sent as a number | Send strings: "12345" |
Invalid locale / Invalid country | e.g. de_DE, german, or de as country | "locale": "de-DE", "country": "DE" |
Invalid profileDetails.age | Age sent as a string or outside 0–130 | "age": 34 (JSON number) |
age / gender missing (from 2026-10-06) | Required profile fields not sent | Always send both; before that date the response carries a profile_fields_required_soon warning instead |
expected a single object, not an array | "profileDetails": [ { … } ] | Send one object: "profileDetails": { … } |
A slow survey match does not return 504 (since 2026-09-23): after 5 seconds the API answers 200 with hasSurveys: false, empty surveys / partlySurveys and a warnings entry with code: "matching_timeout". Treat it like an empty wall and call again on the next page view.
Common validation messages (HTTP 400)
| Message | Cause |
|---|---|
Missing required field: … | Required JSON field missing or empty |
Invalid locale / Invalid country | Format not accepted |
| Redirect URLs message | Complete, screen-out, or cancelled URL or signing secret missing |
Unknown appId or app does not belong to your organization. | Wrong appId |
Rate limits
Each API key may make 600 requests per minute across all /api/v1/publishers/* endpoints. The limit is a fixed one-minute window, counted per API key (not per IP), so one partner can never throttle another.
Over the limit, the API returns HTTP 429 with the standard body and a Retry-After header (seconds until the window resets):
HTTP/1.1 429 Too Many Requests
Retry-After: 23
Content-Type: application/json
{
"requestStatus": "false",
"message": "Rate limit exceeded. Max 600 requests per minute per API key — retry after the Retry-After interval."
}Recommendations:
- Call the surveys endpoint once per panelist page view and cache the response client-side for the session — do not poll.
- Honour
Retry-Afterbefore retrying; treat a burst of 429s as a signal to add client-side throttling. - If you legitimately need more than 600 requests/minute, contact support with your expected peak rate.
Method not allowed (HTTP 405)
{
"error": "Method not allowed",
"allowed": ["POST"]
}Retries
| Status | Retry? |
|---|---|
| 400, 401, 403, 404 | No — fix the request or configuration |
| 429 | Yes — after the Retry-After interval |
| 502, 504 | Yes — backoff and retry |
200 + hasSurveys: false | Not an error — show empty state |
200 + warnings[].code: "matching_timeout" | Show empty state; call again on the next page view (no tight retry loop) |
Profiling URL (legacy)
profilingUrl may appear in a 200 success body for backward compatibility. It is deprecated — do not rely on it in new integrations. Show offer entryLink values instead; base profile questions are collected inline when the panelist starts an offer.
If you still open profilingUrl, treat it like entryLink: HTTPS only, no secrets embedded.
Logging
Log HTTP status, requestStatus, and message so you can quote them to support. Do not log full API keys or signing secrets.