Errors

HTTP status codes the JobsPipe API returns, and how to handle them.

Errors return a JSON body with an error message:

{ "error": "Rate limit exceeded" }

Status codes

Statuserror messageCauseWhat to do
400Invalid search filters: ...A filter name we do not have, or a value of the wrong type.Read the message; it names the field and, for an unknown name, suggests the real one.
401Unauthorized / Invalid API keyMissing, malformed, or revoked key.Check the Authorization header and key validity.
402Monthly request quota exceededYou are out of credits: this month's allowance, or on Free the one-time 1,000 credits.Wait for the monthly reset, buy credits, or upgrade. On Free, pick a package.
403Blocked: ...The account is not allowed to use the API.See 403 Forbidden. Retrying the same request will not help.
404company_not_found / Not FoundNo record for the key, or no such route.On a company lookup or a company tech stack, company_not_found lists candidates; pick one. Costs nothing.
429Rate limit exceededToo many requests in one second for your plan.Back off and retry (see below).
502variesThe request could not be completed.Retry.
504variesThe search timed out.Retry; if it persists, narrow your filters.
500Internal Server ErrorUnexpected server error.Retry; contact support if it persists.

See rate limits & plans for the exact per-second and monthly thresholds.

402 Payment Required

A 402 means the account is out of credits. The body says how many credits were included and used, and carries upgrade_url, the billing page sized to your usage. On Free it also carries checkout_url: a one-time link, valid for seven days, that opens Stripe Checkout for the Builder package (25,000 credits a month) for this account with nothing else to fill in. An agent can hand that link to its user as it is. The same link is emailed once when the credits run out.

{
  "error": "Monthly request quota exceeded",
  "message": "...Open checkout_url to buy the Builder package (25,000 credits a month for $49) in one step.",
  "credits_used": 1000,
  "credits_included": 1000,
  "upgrade_url": "https://jobspipe.dev/dashboard/billing?credits=25",
  "checkout_url": "https://api.jobspipe.dev/v1/checkout/start?t=..."
}

403 Forbidden

A 403 uses the same { "error": "..." } shape, and the message says what to do. It is not a rate limit, so retrying does not clear it.

Blocked account

Accounts that break the signup rules, for example accounts registered with a disposable email address, are refused on every authenticated request (API key, dashboard and MCP):

{
  "error": "Blocked: this account was created with a disposable email address, which JobsPipe does not allow. Sign up again with your own work email, or book a call at https://jobspipe.dev/book to get verified."
}

Sign up again with your own work email, or book a call to get the account verified.

Reading a 400

Unknown filter names are rejected, not ignored. The message names every unknown key and suggests the closest real one:

{
  "error": "Invalid search filters: unknown filters: 'query', 'location'. Did you mean query -> job_title_or, location -> job_location_or?"
}

A value a filter does not accept is reported the same way, with the value you most likely meant when one is clear. A value we do not track (for example an employer type of government) lists the allowed values without a guess:

{
  "error": "Invalid search filters: employer_type_not.0: Invalid enum value. Expected 'employer' | 'agency' | 'broker', received 'staffing'. Did you mean 'agency'?"
}

A body that is not usable JSON says why: it is empty, it has a trailing comma or single quotes (with the line and column), or the filters are wrapped in a string field instead of sent as the JSON object itself.

Numbers and booleans sent as strings are accepted ("posted_at_max_age_days": "10" means 10). An explicit null is treated as an absent field.

Handling 429 with backoff

When you hit the per-second limit, pause and retry with exponential backoff:

import time, requests

def search_with_retry(body, headers, tries=4):
    for i in range(tries):
        resp = requests.post(
            "https://api.jobspipe.dev/v1/jobs/search", headers=headers, json=body
        )
        if resp.status_code != 429:
            resp.raise_for_status()
            return resp.json()
        time.sleep(0.25 * (2 ** i))
    raise RuntimeError("rate limited")
async function searchWithRetry(body, headers, tries = 4) {
  for (let i = 0; i < tries; i++) {
    const resp = await fetch("https://api.jobspipe.dev/v1/jobs/search", {
      method: "POST",
      headers,
      body: JSON.stringify(body),
    });
    if (resp.status !== 429) return resp.json();
    await new Promise((r) => setTimeout(r, 250 * 2 ** i));
  }
  throw new Error("rate limited");
}

Backing off does not help with 402 (out of credits) or 403 (blocked account). For a 402, wait for the monthly reset, buy credits or upgrade; on Free, pick a package. For a 403, see 403 Forbidden.

On this page