Rate limits

PropSocket rate-limits at 150 requests per minute per Organization. That's the number to plan around — not "reasonable limits," not a soft suggestion. Read thex-ratelimit-* headers on every response, honor Retry-After on a 429, and you'll never get surprised in production.

Per Organization, not per key

The limit is accounted at the Organization level. Every API key your Organization holds draws from the same 150 req/min pool — issuing a second key splits the budget, it doesn't double it. If you run several services against PropSocket, they share one bucket; size your concurrency accordingly. Keys being Organization-scoped is covered in authentication.

Throttle, then hard limit

There are two thresholds inside the per-minute window:

UsageBehavior
Up to 80% (≤ 120 req/min)Full speed. No added latency.
80%–100% (120–150 req/min)Throttle. Responses get a 2-second delay to push back before you hit the wall. Requests still succeed.
100% (> 150 req/min)Hard limit. Further requests return 429 until the minute window resets.

The 2-second throttle is a feature, not a failure — it slows you to a sustainable pace before you start collecting 429s. If you see latency climb to ~2s, that's the signal to ease off.

Headers

Every response carries your current standing:

Response headers
HTTP/1.1 200 OK
x-ratelimit-limit: 150
x-ratelimit-remaining: 112
X-Request-ID: req_01HX9P3K2N7QZRWY4B8MJ5VCDF
  • x-ratelimit-limit — your ceiling (150 by default).
  • x-ratelimit-remaining — requests left in the current window.

Watch x-ratelimit-remaining and slow down as it approaches zero, rather than waiting for the 429. The custom limit shown in x-ratelimit-limit reflects any per-contract override on your Organization.

The 429 response

When you cross 100%, you get a 429 with a Retry-After header in seconds. Sleep for exactly that long — don't guess a backoff, the server already told you. The body is the standard RFC 7807 problem+json shape.

429 Too Many Requests
HTTP/1.1 429 Too Many Requests
x-ratelimit-limit: 150
x-ratelimit-remaining: 0
Retry-After: 23
Content-Type: application/problem+json

{
  "type": "https://docs.propsocket.io/errors/rate_limit_exceeded",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Rate limit exceeded. Retry after 23 seconds.",
  "instance": "/v1/units",
  "request_id": "req_01HX9P3K2N7QZRWY4B8MJ5VCDF"
}

Handle 429 with backoff

A minimal client that honors Retry-After and retries transparently:

Honor Retry-After
import os, time, requests

BASE = "https://api.propsocket.io/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PROPSOCKET_API_KEY']}"}

def get(path, **params):
    while True:
        resp = requests.get(f"{BASE}/{path}", headers=HEADERS, params=params)
        if resp.status_code == 429:
            wait = int(resp.headers.get("Retry-After", "1"))
            time.sleep(wait)  # honor the server's wait, don't guess
            continue
        resp.raise_for_status()
        return resp.json()

print(get("units", limit=100)["meta"])

Backfills and large loads

150 req/min is plenty for steady-state reads, but a cold backfill of a large warehouse needs a plan. The math is simple: at the max page size of 100 records per request (seepagination), 150 requests/minute moves up to 15,000 records per minute — roughly 900,000 per hour if you stay just under the throttle.

  • Use the max page size. limit=100 means fewer requests per record. This is the single biggest lever.
  • Page sequentially per entity, sorted by created_at:asc. Don't fan out parallel offsets against one entity — that's a correctness bug, not just a throughput one (see pagination).
  • Parallelize across entities, not within one. One worker per entity (properties, units, residents, leases) is safe and shares the same pool — keep total concurrency low enough to stay under 120 req/min and avoid the throttle.
  • Cap your client at ~2 requests/second as a simple ceiling — that's 120/min, below the throttle threshold, leaving headroom for your steady-state traffic.

For a 100k-unit load, that's well under ten minutes of wall-clock time at full page size. Backfill once with created_at:asc, then keep current withwebhooks instead of re-polling — webhooks are on theScale plan and above (pricing).

Next

Decode the 429 body in the error reference, or plan a clean backfill with the pagination guide.