Docs / API Error Reference
API Error Reference
Every Foglift API error response includes a machine-readable code field, a docs_url linking to the relevant section below, and a request_id for support correlation.
Response shape
All error responses follow this structure. The details field is present only when the error includes additional context (field validation failures, brand lists, etc.). Every response also includes a Link header (RFC 5988) pointing to this page, and an x-request-id header.
{
"error": "Human-readable error message.",
"code": "error_code_snake_case",
"docs_url": "https://foglift.io/docs/api-errors#error_code_snake_case",
"request_id": "req_...",
"details": {} // optional
}
HTTP headers:
Link: <https://foglift.io/docs/api-errors#error_code_snake_case>; rel="help"
x-request-id: req_...cannot_delete_last_brand
400Default message
Cannot remove your only brand. Create a new one first.
What triggers it
You called DELETE on your only remaining brand. Every account must have at least one brand.
How to recover
Create a new brand via POST /api/v1/brands before deleting the existing one.
curl -X DELETE https://foglift.io/api/v1/brands/8f4c2a10-6c2f-4b2e-b2a1-4c5e7f8a9b10 \
-H "Authorization: Bearer sk_fog_..."{
"error": "Cannot remove your only brand. Create a new one first.",
"code": "cannot_delete_last_brand",
"docs_url": "https://foglift.io/docs/api-errors#cannot_delete_last_brand",
"request_id": "req_m3k7x9..."
}Related: /docs#rest-api
feature_gated
403Default message
This feature is not available on your current plan.
What triggers it
You exceeded a per-plan usage limit — for example the brand limit, API key limit, or scheduled-scan limit for your plan. Plan-gated features (batch scans, sitemap scans, team seats, query fanouts) return 402 payment_required instead; this code covers limit-class gates where the feature itself is available but capped.
How to recover
Upgrade your plan at /dashboard/settings or /pricing for higher limits, or remove unused resources (brands, keys) to free up capacity. The details field reports your limit and current usage when available.
curl -X POST https://foglift.io/api/v1/brands \
-H "Authorization: Bearer sk_fog_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://newbrand.com"}'{
"error": "Your Launch plan allows up to 3 brands. Upgrade to add more.",
"code": "feature_gated",
"docs_url": "https://foglift.io/docs/api-errors#feature_gated",
"request_id": "req_p8r2v5...",
"details": {
"limit": 3,
"used": 3
}
}Related: /pricing, /docs#rate-limits
forbidden
403Default message
You do not have permission to access this resource.
What triggers it
Your authenticated session or API key does not have permission to access the requested resource. This can happen when you try to access another account's data or a restricted admin endpoint.
How to recover
Verify that the API key belongs to the account that owns the resource. If you are using session-based auth, confirm you are logged in to the correct account.
curl https://foglift.io/api/v1/brands/8f4c2a10-6c2f-4b2e-b2a1-4c5e7f8a9b10 \
-H "Authorization: Bearer sk_fog_..."{
"error": "You do not have permission to access this resource.",
"code": "forbidden",
"docs_url": "https://foglift.io/docs/api-errors#forbidden",
"request_id": "req_j4n1w8..."
}Related: /docs#rate-limits
idempotency_key_conflict
409Default message
A request with this idempotency key has already been processed.
What triggers it
You sent a POST request with an Idempotency-Key header that matches a previously completed request. The original response is returned instead of processing a duplicate.
How to recover
If you intended a new request, generate a fresh idempotency key (a UUID works well). If you are retrying a failed request, use the same key to get the original response safely.
curl -X POST https://foglift.io/api/v1/ai-visibility \
-H "Authorization: Bearer sk_fog_..." \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{"prompts": ["best SEO tool"]}'{
"error": "A request with this idempotency key has already been processed.",
"code": "idempotency_key_conflict",
"docs_url": "https://foglift.io/docs/api-errors#idempotency_key_conflict",
"request_id": "req_q5s3t7..."
}Related: /docs#ai-visibility-api
insufficient_tokens
402Default message
Insufficient token balance for this operation.
What triggers it
Your monthly AI Visibility token balance cannot cover this request. Each AI engine query costs tokens at different rates: Gemini (1), ChatGPT (3), AI Overview (3), Claude (5), Perplexity (5).
How to recover
Wait for your token balance to reset at the start of your next billing cycle. Alternatively, upgrade your plan for a higher monthly token allowance, or disable expensive engines via POST /api/v1/models to stretch your remaining budget.
curl -X POST https://foglift.io/api/v1/ai-visibility \
-H "Authorization: Bearer sk_fog_..." \
-H "Content-Type: application/json" \
-d '{"prompts": ["best AI search tool"], "models": ["chatgpt","claude"]}'{
"error": "Insufficient token balance for this operation.",
"code": "insufficient_tokens",
"docs_url": "https://foglift.io/docs/api-errors#insufficient_tokens",
"request_id": "req_a9c4d2..."
}Related: /pricing, /docs/ai-assistant-setup
internal_error
500Default message
An internal error occurred. Please try again or contact support.
What triggers it
An unexpected server-side failure. This is not caused by your request payload or authentication.
How to recover
Retry the request after a short delay (start with 1 second, then back off exponentially). If the error persists across multiple retries, contact support@foglift.io with the request_id from the response.
curl https://foglift.io/api/v1/scan?url=https://example.com \
-H "Authorization: Bearer sk_fog_..."{
"error": "An internal error occurred. Please try again or contact support.",
"code": "internal_error",
"docs_url": "https://foglift.io/docs/api-errors#internal_error",
"request_id": "req_f7h2k9..."
}Related: /docs#rest-api
invalid_api_key
401Default message
The provided API key is invalid or has been revoked.
What triggers it
The API key in your Authorization header (or X-API-Key header) does not match any active key. The key may have been deleted, rotated, or typed incorrectly.
How to recover
Generate a new API key at /dashboard/settings. API keys use the sk_fog_ prefix. Make sure you are passing the full key, not a truncated version.
curl https://foglift.io/api/v1/scan?url=https://example.com \
-H "Authorization: Bearer sk_fog_INVALID"{
"error": "The provided API key is invalid or has been revoked.",
"code": "invalid_api_key",
"docs_url": "https://foglift.io/docs/api-errors#invalid_api_key",
"request_id": "req_b3e6g1..."
}Related: /docs#rate-limits, /dashboard/settings
invalid_payload
400Default message
The request body is invalid or malformed.
What triggers it
The JSON body of your request could not be parsed, or the top-level structure does not match the expected schema. Common causes: missing Content-Type header, trailing commas in JSON, or sending form-encoded data instead of JSON.
How to recover
Confirm your request includes Content-Type: application/json and the body is valid JSON. Use a JSON linter to check for syntax errors.
curl -X POST https://foglift.io/api/v1/prompts \
-H "Authorization: Bearer sk_fog_..." \
-H "Content-Type: application/json" \
-d '{invalid json}'{
"error": "The request body is invalid or malformed.",
"code": "invalid_payload",
"docs_url": "https://foglift.io/docs/api-errors#invalid_payload",
"request_id": "req_c4f7h2..."
}Related: /docs#rest-api
payment_required
402Default message
Payment is required before this resource can be accessed.
What triggers it
Two triggers share this code. (1) Plan gate: you called a feature your current plan does not include — batch scans, sitemap scans, team seats, query fanouts, Win Rate, watched pages, knowledge bases, or Ask Foglift. These responses carry a details payload identifying the feature and the minimum plan that unlocks it. (2) Unpaid checkout: you attempted to retrieve a paid resource, such as a full scan report, before the checkout session was paid or completed.
How to recover
For plan gates, branch on details.feature and details.required_plan — upgrade at details.upgrade_url to unlock the feature. For unpaid checkout, complete payment and retry with the paid checkout session ID. If payment succeeded but the error persists, contact support with the request_id.
curl -X POST https://foglift.io/api/v1/scan/batch \
-H "Authorization: Bearer sk_fog_..." \
-H "Content-Type: application/json" \
-d '{"urls": ["https://a.com", "https://b.com"]}'{
"error": "Batch scanning requires a Launch plan or higher.",
"code": "payment_required",
"docs_url": "https://foglift.io/docs/api-errors#payment_required",
"request_id": "req_p2q5r8...",
"details": {
"feature": "batch_scan",
"required_plan": "launch",
"upgrade_url": "https://foglift.io/pricing"
}
}Related: /pricing, /docs#rest-api
missing_workspace_id
400Default message
brand_id is required for this endpoint.
What triggers it
You called an endpoint that operates on a specific brand, but did not include the brand_id parameter. This only occurs on accounts with multiple brands where the system cannot infer which brand you mean.
How to recover
Include brand_id as a query parameter or in the request body. workspace_id is accepted as a legacy alias. Get your brand IDs from GET /api/v1/brands or from /dashboard/developer.
curl https://foglift.io/api/v1/ai-visibility/results \
-H "Authorization: Bearer sk_fog_..."{
"error": "brand_id is required for this endpoint.",
"code": "missing_workspace_id",
"docs_url": "https://foglift.io/docs/api-errors#missing_workspace_id",
"request_id": "req_d5g8j3..."
}Related: /docs#ai-visibility-api
multi_brand_disambiguation_required
400Default message
Account has multiple brands. Pass brand_id to disambiguate.
What triggers it
Your account has more than one brand and the endpoint needs to know which one to target. Unlike missing_workspace_id, this error includes a list of your available brands in the details field to help you pick the right one.
How to recover
Choose the correct brand_id from the brands array in the error response, then resend your request with that ID. workspace_id is accepted as a legacy alias.
curl -X POST https://foglift.io/api/v1/ai-visibility \
-H "Authorization: Bearer sk_fog_..." \
-H "Content-Type: application/json" \
-d '{"prompts": ["best AI tool"]}'{
"error": "Account has multiple brands. Pass brand_id to disambiguate.",
"code": "multi_brand_disambiguation_required",
"docs_url": "https://foglift.io/docs/api-errors#multi_brand_disambiguation_required",
"request_id": "req_e6h9k4...",
"details": {
"brands": [
{ "id": "8f4c2a10-6c2f-4b2e-b2a1-4c5e7f8a9b10", "name": "Brand A", "domain": "branda.com" },
{ "id": "1f2e3d4c-5b6a-4789-8abc-0def12345678", "name": "Brand B", "domain": "brandb.com" }
]
}
}Related: /docs#ai-visibility-api, /docs#mcp-server
not_found
404Default message
The requested resource was not found.
What triggers it
The endpoint path is valid but the specific resource (brand, prompt, scan result) does not exist. The resource may have been deleted or the ID may be incorrect.
How to recover
Verify the resource ID. For brands, use GET /api/v1/brands to list your current brands. For prompts, use GET /api/v1/prompts to list active prompts.
curl https://foglift.io/api/v1/brands/00000000-0000-4000-8000-000000000000 \
-H "Authorization: Bearer sk_fog_..."{
"error": "The requested resource was not found.",
"code": "not_found",
"docs_url": "https://foglift.io/docs/api-errors#not_found",
"request_id": "req_g7j0m5..."
}Related: /docs#rest-api
prompt_already_exists
409Default message
This prompt already exists for the brand.
What triggers it
You attempted to add a prompt that is already being tracked for the target brand. Prompt text is compared case-insensitively after trimming whitespace.
How to recover
Check your existing prompts with GET /api/v1/prompts?brand_id=... before adding. workspace_id is accepted as a legacy alias. If you want to re-run the prompt, use POST /api/v1/ai-visibility instead of creating a duplicate.
curl -X POST https://foglift.io/api/v1/prompts \
-H "Authorization: Bearer sk_fog_..." \
-H "Content-Type: application/json" \
-d '{"prompt": "best AI search tool", "brand_id": "8f4c2a10-6c2f-4b2e-b2a1-4c5e7f8a9b10"}'{
"error": "This prompt already exists for the brand.",
"code": "prompt_already_exists",
"docs_url": "https://foglift.io/docs/api-errors#prompt_already_exists",
"request_id": "req_h8k1n6..."
}Related: /docs#ai-visibility-api
rate_limited
429Default message
Too many requests. Please retry after the rate limit window resets.
What triggers it
You have exceeded your daily scan quota or monthly request limit. Limits vary by plan: Free (10/day, 100/month), Launch (50/day, 1,000/month), Growth (200/day, 5,000/month), Enterprise (1,000/day, 25,000/month).
How to recover
Wait for the rate limit window to reset at 00:00 UTC (daily limits) or at the start of your billing cycle (monthly limits). Cache scan results in your application to avoid duplicate requests. Upgrade your plan for higher limits.
curl https://foglift.io/api/v1/scan?url=https://example.com \
-H "Authorization: Bearer sk_fog_..."{
"error": "Too many requests. Please retry after the rate limit window resets.",
"code": "rate_limited",
"docs_url": "https://foglift.io/docs/api-errors#rate_limited",
"request_id": "req_i9l2o7..."
}Related: /pricing, /docs#rate-limits
unauthenticated
401Default message
Authentication required. Provide a valid session or API key (Authorization: Bearer sk_fog_...).
What triggers it
The request did not include an API key or session cookie. Most Foglift API endpoints require authentication. The unauthenticated GET /api/v1/scan endpoint (up to 10 scans/day by IP) is the main exception.
How to recover
Add an Authorization header with your API key: Authorization: Bearer sk_fog_.... Generate a key at /dashboard/settings. The CLI and MCP server handle this automatically when configured.
curl https://foglift.io/api/v1/ai-visibility/results{
"error": "Authentication required. Provide a valid session or API key (Authorization: Bearer sk_fog_...).",
"code": "unauthenticated",
"docs_url": "https://foglift.io/docs/api-errors#unauthenticated",
"request_id": "req_k0m3p8..."
}Related: /docs#rate-limits, /dashboard/settings
validation_failed
400Default message
One or more fields failed validation.
What triggers it
The request body is valid JSON but one or more field values do not pass validation rules. The details field lists which fields failed and why.
How to recover
Check the details object in the error response for specific field-level messages. Fix the invalid fields and retry.
curl -X POST https://foglift.io/api/v1/prompts \
-H "Authorization: Bearer sk_fog_..." \
-H "Content-Type: application/json" \
-d '{"prompt": "", "brand_id": "8f4c2a10-6c2f-4b2e-b2a1-4c5e7f8a9b10"}'{
"error": "One or more fields failed validation.",
"code": "validation_failed",
"docs_url": "https://foglift.io/docs/api-errors#validation_failed",
"request_id": "req_l1n4q9...",
"details": {
"fields": {
"prompt": "Prompt text is required and must be between 3 and 500 characters."
}
}
}Related: /docs#rest-api
workspace_access_denied
403Default message
Workspace not found or access denied.
What triggers it
The brand_id you provided does not exist or does not belong to your account. This is distinct from forbidden, which covers general permission failures. This error is specific to brand-scoped endpoints.
How to recover
List your brands with GET /api/v1/brands and use a valid brand_id from that list. workspace_id is accepted as a legacy alias. If you recently deleted the brand, it is no longer accessible.
curl https://foglift.io/api/v1/prompts?brand_id=00000000-0000-4000-8000-000000000000 \
-H "Authorization: Bearer sk_fog_..."{
"error": "Workspace not found or access denied.",
"code": "workspace_access_denied",
"docs_url": "https://foglift.io/docs/api-errors#workspace_access_denied",
"request_id": "req_n2o5r0..."
}Related: /docs#rest-api
Need help?
If you receive an error that is not listed here, or if an error persists after following the recovery steps, email support@foglift.io with the request_id from the error response. The request ID lets us locate the exact server-side log entry for your failed call.