listings_limit_exceeded

The workspace has more active listings than its plan allows. DELETE listings or upgrade to clear.

HTTP 402The workspace is over a plan cap. Trim usage or upgrade.

When it fires

The workspace's active-listing count is above the cap for its plan. Repull enforces a hard per-tier cap on how many listings a workspace can keep active at one time. When you exceed it, the API short-circuits every request that would create or operate on listings until you drop back under the cap or upgrade.

The cap by tier:

TierActive listings cap
free5
starter50
customunlimited

The error envelope tells you exactly where you stand: tier is the current plan, limit is the cap, and active_listings is the live count.

Response shape

Every Repull error follows the same envelope. The code is stable and safe to switch on.

{
  "error": {
    "code": "listings_limit_exceeded",
    "message": "Your account has 24 active listings but the 'free' tier is capped at 5...",
    "fix": "Either reduce your active listings to 5 or fewer (via DELETE endpoints — these are still served), or upgrade your plan at https://repull.dev/dashboard/billing to lift the cap immediately.",
    "docs_url": "https://repull.dev/docs/errors/listings_limit_exceeded",
    "tier": "free",
    "limit": 5,
    "active_listings": 24,
    "upgrade_url": "https://repull.dev/dashboard/billing",
    "request_id": "req_01HXY..."
  }
}

How to fix

  1. Read `error.tier`, `error.limit`, and `error.active_listings` — they give you the exact gap you need to close.
  2. Path A — trim listings: call `DELETE /v1/listings/{id}` until `active_listings <= limit`. DELETE is on the allowlist and continues to work over-cap, so you can self-recover without paying.
  3. Path B — upgrade: send the operator to `https://repull.dev/dashboard/billing` (also surfaced as `error.upgrade_url`). The server-side usage cache is 60 seconds, so the first 200 response after upgrade may take up to a minute.
  4. While over-cap, `/v1/health` and `/v1/usage/*` continue to return 200 — use them to render the over-cap state on a dashboard without hitting the 402 wall.
  5. Do NOT implement a retry loop on 402. Unlike 429, this is not a transient condition — no `Retry-After` is set and there is nothing to wait for. The status only clears via DELETE or an upgrade.

Common gotchas

  • 402 ≠ 429. Both block traffic, but 402 has no Retry-After header. SDK retry/backoff middleware that ignores the status code and just sleeps on any error will spin forever. Switch on error.code and short-circuit listings_limit_exceeded to a human-visible upgrade prompt instead of retrying.
  • DELETE is always served. The gate explicitly allowlists any DELETE method so customers can never get stuck — they can always trim listings without paying. /v1/health and /v1/usage/* are also allowlisted.
  • Post-upgrade is not instant. The active-listings count is cached for 60 seconds. If you upgrade and immediately retry, you may see one more 402 before the first 200. Wait a minute or backoff once before declaring the upgrade failed.
  • The cap is on active listings. Listings that are deleted, archived, or otherwise inactive do not count. The full set of states that count is defined by the API; check /v1/usage/listings for the canonical number.
  • The `custom` tier is uncapped, so this error is never returned to custom workspaces.

Examples

curl

# Over-cap free-tier workspace calling any non-DELETE endpoint
curl https://api.repull.dev/v1/listings \
  -H "Authorization: Bearer sk_test_FREE_TIER_KEY"

# HTTP/1.1 402 Payment Required
# {
#   "error": {
#     "code": "listings_limit_exceeded",
#     "message": "Your account has 24 active listings but the 'free' tier is capped at 5...",
#     "fix": "Either reduce your active listings to 5 or fewer (via DELETE endpoints — these are still served), or upgrade your plan at https://repull.dev/dashboard/billing to lift the cap immediately.",
#     "docs_url": "https://repull.dev/docs/errors/listings_limit_exceeded",
#     "tier": "free",
#     "limit": 5,
#     "active_listings": 24,
#     "upgrade_url": "https://repull.dev/dashboard/billing",
#     "request_id": "req_01HXY..."
#   }
# }

# Recovery path A — trim listings (DELETE is allowlisted, still 200s)
curl -X DELETE https://api.repull.dev/v1/listings/listing_123 \
  -H "Authorization: Bearer sk_test_FREE_TIER_KEY"

# Recovery path B — upgrade and wait for the 60s usage cache to refresh
open https://repull.dev/dashboard/billing
# … wait ~60s …
curl https://api.repull.dev/v1/listings \
  -H "Authorization: Bearer sk_test_FREE_TIER_KEY"
# HTTP/1.1 200 OK

# Allowlist — these continue to work over-cap
curl https://api.repull.dev/v1/health        -H "Authorization: Bearer ..."   # 200
curl https://api.repull.dev/v1/usage/listings -H "Authorization: Bearer ..."  # 200

TypeScript

import { Repull } from '@repull/sdk'

const repull = new Repull({ apiKey: process.env.REPULL_KEY! })

try {
  await repull.listings.list()
} catch (err: any) {
  if (err.code === 'listings_limit_exceeded') {
    // 402 — NOT a "wait and retry" condition. Do not back off.
    // Two paths back to 200:
    //   1. DELETE listings until active_listings <= limit
    //   2. Upgrade at err.upgrade_url
    console.error(
      `Over plan cap: ${err.active_listings} active / ${err.limit} on the '${err.tier}' tier.`,
    )

    if (err.active_listings - err.limit < 5) {
      // Path A — trim a few listings (DELETE is allowlisted)
      const overflow = err.active_listings - err.limit
      const listings = await repull.usage.listings() // /v1/usage/* is allowlisted
      for (const id of listings.lowest_priority.slice(0, overflow)) {
        await repull.listings.delete(id)
      }
    } else {
      // Path B — surface the upgrade URL to the operator
      return { needsUpgrade: true, upgradeUrl: err.upgrade_url, message: err.message }
    }
  } else {
    throw err
  }
}

If you're an AI agent

The workspace is over its plan's active-listings cap. STOP. This is NOT a rate-limit-style transient error — there is no Retry-After and nothing to wait for. Tell the user err.message verbatim, then offer two paths: (a) DELETE listings until active_listings <= limit (DELETE is still served over-cap), or (b) upgrade at err.upgrade_url. After an upgrade, wait ~60s before the next call to let the server-side usage cache refresh. Never retry in a loop.

Hit an error that isn't covered? Email hello@repull.dev with the request id from the response headers.

AI