Documentation

REST API Errors

Every error code the MetricPeek REST API returns, with its HTTP status, what it means and what to do next.

Every refusal of the REST API comes back as application/problem+json, in one shape, and this page explains each code it can carry. The type of an error points at the section of this page named after its code, so .../docs/api-errors#rate_limited opens rate_limited. The same codes appear when the MCP server refuses a tool call, so this page serves both.

Reading an error

{
  "type": "<this page>#insufficient_credits",
  "title": "The team does not have enough credits for this action.",
  "status": 402,
  "detail": "The team does not have enough credits for this action.",
  "code": "insufficient_credits"
}
Field What it holds
code The reason, as a fixed word. This is what a script branches on, and it never changes within v1.
status The HTTP status, the same as the status line of the answer.
title A short sentence about the code, for a person reading a log.
detail A sentence about this one refusal. It may be reworded at any time, so a script never matches on it.
type The address of the code's section on this page.
context Present when there is more to say: identifiers such as reason, scope, argument or retry_after_seconds, never a value that was sent.

A 429 and some other refusals also carry a Retry-After header with the number of seconds to wait. Every error carries Cache-Control: no-store.

Authentication and access

These come before any work, from the checks every call passes on its way in. A 403 here is only ever given to a valid key, so it tells the key's own holder something they can act on.

unauthorized · 401

The request did not authenticate. Every failure gets this same answer on purpose, so a response never tells a stranger which keys exist: no Authorization header, a key that is mistyped, revoked or expired, a key sent anywhere but the header, and the sign-in token of an AI assistant, which the REST API does not accept. The answer carries WWW-Authenticate: Bearer realm="api".

What to do: send the team's API key in the Authorization header, after the word Bearer and a space. The API keys card in Settings → API & MCP of the key's team shows whether the key is still Active, and a revoked or expired key is replaced with a new one. A script that keeps retrying the same wrong key is slowed down with rate_limited.

rest_disabled · 403

The MetricPeek operator has the REST API switched off, either because it has not opened yet or for a while. The key is valid, nothing about it needs changing, and the same key keeps working on the MCP server.

What to do: nothing on the key. Calls work again once the REST API is switched back on.

mcp_disabled_for_team · 403

The team owner has switched API & MCP access off for the team, which stops every assistant and every API key in it. The keys are kept as they are.

What to do: the owner turns the switch back on, on the API & MCP access card in Settings → API & MCP of that team, and the key works again from its next call.

plan_lapsed · 403

The team's plan no longer includes API keys, which happens after a move to Free. The key is marked Suspended and kept as it is.

What to do: the key starts working again by itself once the team is on a plan that includes API keys. The pricing page lists those plans.

connection_suspended · 403

MetricPeek has paused this key, or paused the service or one kind of action, such as spending or publishing, for a while. The key is kept.

What to do: wait and try later, or ask support why it was paused. Nothing about the key needs changing.

team_closed · 403

The team the key belongs to has been closed, and its keys no longer act for it.

What to do: a key of another team is needed. Support can help when the closing was not expected.

account_not_approved · 403

The MetricPeek account of the person the key acts for, the team owner, is on hold at MetricPeek. Nothing is changed on the key.

What to do: the owner contacts support. The key works again once the account is active.

membership_lost · 403

The person the connection acts for is no longer a member of the team. The connection is revoked at that moment and does not work again.

What to do: the team's current owner creates a new key, as Create a key describes.

ownership_lost · 403

The person who created the key is no longer the team's owner, for example after the team was handed to someone else. The key is revoked at that moment, because it would otherwise spend the team's credits within limits the new owner never agreed to.

What to do: the new owner creates the keys the team still needs.

role_lost · 403

The person an AI assistant connection belongs to no longer has a role that can publish in the team, and the connection is revoked. This code is given to assistant connections, and an API key gets ownership_lost instead.

What to do: a member who can publish connects the assistant again.

Addresses and methods

These are answered before any authentication, with the same bytes whether the key is valid, wrong or missing.

endpoint_not_found · 404

No endpoint of the API has this path. A misspelt path and an identifier that does not have the form the path expects, such as letters where a number belongs, all end here.

What to do: compare the path with the API reference. An item that is simply missing or belongs to another team gets not_found instead.

method_not_allowed · 405

The path exists, but not with this HTTP method, for example GET on a path that only takes POST. The Allow header lists the methods it takes.

What to do: send the request with one of the methods in Allow.

Access levels

A key is created at the access level Read only, Drafts or Full, and a call beyond it is refused before anything is read or charged. The owner changes the level with Change access in the key's ⋮ menu.

read_not_allowed · 403

The connection may not read this team's data. Every access level includes reading, so a key does not get this code in normal use.

What to do: check that the call is made with the intended key, then ask support if it persists.

write_not_allowed · 403

The connection may not create or change anything in the team. A key at Read only gets it on any write.

What to do: the owner raises the key to Drafts or Full with Change access.

publish_not_allowed · 403

The connection may not publish. A key below Full gets it when scheduling, publishing or retrying a post, and when changing a post that is already scheduled.

What to do: the owner raises the key to Full with Change access.

spend_not_allowed · 403

The connection, or the role of the person it acts for, may not spend the team's credits. A key below Full gets it on every paid endpoint, including a repeat that would have cost nothing, because spending is a property of the endpoint.

What to do: the owner raises the key to Full with Change access. This is a question of permission, not of money, and the team's balance is not the problem.

Limits and credits

rate_limited · 429

Too many requests or calls within a short time. The Retry-After header and context.retry_after_seconds give the number of seconds to wait, and context.scope names the limit:

  • credential, too many requests with this key within a minute, across the REST API and the MCP server together;
  • connection and provider, too many calls within a minute, the second one for calls that reach out to the social networks;
  • jobs_in_flight, the team already has as many background jobs running as it may;
  • failed_authentication, the same wrong key tried too many times;
  • open_upload_batches and open_upload_links, too many uploads still waiting for their files.

What to do: wait for Retry-After, then send the same request again. A loop that asks for a job's status every few seconds, rather than as fast as it can, stays clear of it.

daily_call_limit_reached · 429

The team's daily allowance of free calls is used up, across every assistant and API key in the team. Calls that charge nothing draw on it, and so do paid calls that ended up charging nothing. Retry-After counts down to midnight in the team's timezone.

What to do: wait for midnight in the team's timezone. Paid calls keep working unless paid calls that ended up free used up the allowance on their own. The allowance for each plan is on the pricing page.

daily_credit_limit_reached · 429

A paid call would have crossed a daily credit limit, and nothing was charged. context.scope says which one: team for the team's limit on what all its assistants and keys spend together, connection for this key's own limit. The team still has its credits, and Retry-After counts down to midnight in the team's timezone.

What to do: wait for midnight, or have the owner raise the limit. The team's limit is on the API & MCP access card and the key's own with Change access, both in Settings → API & MCP of the key's team. How the limits are worked out is under Credits and limits.

insufficient_credits · 402

The team's wallet does not hold enough credits for this call, and nothing was charged.

What to do: GET /credits shows the balance when the key's role may see it. Credits are added in the app by the team owner, with the app switched to the team the key belongs to.

plan_limit_reached · 403

The team's plan does not include this, or one of its limits is used up. Examples are a full Media Library, the monthly post allowance, the number of hashtag lists or tracked reports, and analytics the plan does not include. The detail sentence names which.

What to do: GET /limits shows the plan's limits. The call works once there is room again, for example after files are removed from the Media Library or a new month begins, or on a plan that includes it.

Requests and items

validation_failed · 422

The request did not match what the endpoint accepts, and nothing was done. context.reason says what was wrong, and context.argument names the argument when there is one. The reasons the REST API adds on top of each endpoint's own checks are:

  • unknown_argument, a name the endpoint does not take, and argument_given_twice, an argument that is already part of the path and was sent again in the query string or in the body;
  • body_not_allowed, a GET with a body, and arguments_in_query, a write with a query string;
  • body_not_a_json_object, a write body that is not a JSON object;
  • not_an_integer, not_a_number, not_a_boolean, not_a_list, not_a_single_value and not_expressible_in_a_query, a value that does not fit the argument's type;
  • secret_in_argument, a call that is not a read and whose arguments contain a MetricPeek API key or an upload secret. Nothing is saved, sent or published, and the answer never repeats the secret. An API key that got into a request body this way should be revoked;
  • idempotency_key_required, a paid endpoint called without the Idempotency-Key header;
  • idempotency_key_not_accepted, idempotency_key_given_twice, idempotency_key_empty and idempotency_key_too_long, a key the endpoint does not take, a key sent both as a header and in the body, an empty one, or one longer than the endpoint accepts.

What to do: correct the request as the reason says, using the API reference for the endpoint's arguments.

not_found · 404

No such item is available to this key. An item that does not exist and an item of another team get exactly the same answer, so an identifier never reveals whether another team has it.

What to do: use the identifiers the API itself returned for this team, from the matching list endpoint.

folder_not_found · 404

The folder_id names no folder of this team's Media Library. It is answered like not_found, with its own code only so a script knows which argument to fix.

What to do: send a folder id this team has, or leave folder_id out.

idempotency_key_reused · 422

The Idempotency-Key cannot be used for this request, and nothing was done. context.reason says why:

  • without a reason, the same key was already used from this key within the last 24 hours for a call of this endpoint with different arguments;
  • charge_key_spent, on a paid endpoint, the key already paid for an earlier call whose result is no longer kept, so nothing was charged or started.

What to do: send the request with a new Idempotency-Key. One key belongs to one request, as Repeating a request safely explains.

idempotency_key_in_progress · 409

The first call with this Idempotency-Key is still running, and nothing was started a second time.

What to do: wait for Retry-After, then send the same request with the same key. It returns the first call's result.

Publishing

preflight_blocked · 422

The post did not pass validation against the networks' rules, and it was not sent anywhere.

What to do: GET /posts/{post_id}/validation lists the problems for each account. Fix them with PATCH /posts/{post_id}, then schedule or publish again.

awaiting_client_approval · 403

The post waits for the client's approval, and nothing can publish or schedule it before the client decides. The key's permissions are not the problem.

What to do: wait for the client's decision. The post's page in the app shows what it is waiting for, and Sharing with Clients describes how approval works.

provider_unavailable · 502

The service that delivers posts to the networks did not confirm the post. Nothing is known to have been published, but the post may still have gone out.

What to do: check the post with GET /posts/{id} before publishing it again, so it does not go out twice.

Server

internal_error · 500

The call failed on MetricPeek's side rather than because of the request, and part of it may already have taken effect.

What to do: check the result first, for example the post, the report or the job. Repeat the call only with the same Idempotency-Key it was first sent with, so it is not done or charged twice. Support can look into a call that keeps failing.

What this does not do

  • An error never says whether a key exists. Every failure to authenticate is the same 401.
  • An error never suggests buying credits or changing the plan. It states the reason and stops.
  • An error never echoes a value that was sent. context carries names and identifiers only.
  • A detail sentence is not a contract. Only code and status are, and new codes may be added within v1.

When something looks wrong

A code is not on this page. New codes can be added within v1. A script that meets a code it does not know acts on the HTTP status, and support can explain the code.

The same call keeps failing with internal_error. It is worth sending to support with the endpoint, the code and roughly when it happened, never with the key.

Every call gets 401 although the key was just created. The key is usually missing from the request, for example because the environment variable holding it is empty, or it was sent somewhere other than the Authorization header. Authentication describes the header.

Everything you need to grow on social media.

Analyze your competitors, discover winning ideas, create better content with AI and publish everywhere from one workspace.