Rate Limiting

The Addepar API enforces rate limits at the firm level. Every API key and OAuth application within the same firm shares a single request budget. Understanding this shared model is essential before building any integration that makes concurrent or scheduled requests.

Budget model

Rate limits apply per firm, across all credentials. If your firm has three integrations (a CRM sync, a reporting pipeline, and an interactive dashboard), all three draw from the same pool.

WindowBudgetConsequence
15-minute rolling50 requests429 Too Many Requests when exceeded
24-hour rolling1,000 requests429 Too Many Requests when exceeded
Per-request timeout60 seconds400 Bad Request if the server cannot respond in time

The 15-minute window constrains burst patterns. The 24-hour window constrains total daily volume. Both run simultaneously.

When either limit is exceeded, the response includes an X-RateLimit-Retry-After header with the number of seconds until the next request will be accepted. Requests sent before that time will continue to receive 429.

If a single request takes longer than 60 seconds to process server-side (common with large portfolio queries or entity batch operations), the server cancels it and returns 400 Bad Request. This is not a rate limit violation. It does not consume budget. Restructure the request (reduce page[limit], narrow the date range, reduce query columns) and retry.

Proactive rate limit header

When your usage reaches approximately 85% of the rate limit threshold, the API includes a RateLimit response header conforming to the IETF draft standard:

RateLimit: limit=1000, remaining=150, reset=3600
FieldMeaning
limitMaximum requests allowed in the current window
remainingRequests left before the limit is reached
resetSeconds until the window resets

This header does not appear on every response. It activates only when you approach the threshold. The absence of the header means you are well within budget.

Implementation pattern:

import time

def make_request(url, headers):
    response = requests.get(url, headers=headers)

    if response.status_code == 429:
        wait = int(response.headers.get('X-RateLimit-Retry-After', 60))
        time.sleep(wait)
        return make_request(url, headers)

    ratelimit = response.headers.get('RateLimit')
    if ratelimit:
        remaining = parse_remaining(ratelimit)
        if remaining < 10:
            time.sleep(5)  # Proactive backoff

    return response

Exempt endpoints

GET requests to these endpoints do not count toward either rate limit window:

EndpointWhy exempt
GET /v1/jobsJob polling requires frequent requests to detect completion
GET /v1/transaction_jobsSame polling pattern for transaction batch jobs
GET /v1/users/meIdentity verification is lightweight and common at session start
GET /v1/importsImport status polling follows the same pattern as jobs

Only GET requests to these paths are exempt. POST, PATCH, and DELETE to the same resources consume budget normally.

This exemption exists because the Jobs async pattern requires polling. A typical job workflow makes 1 POST (submit) then 10-50 GETs (poll until complete). Without the exemption, a single batch operation would consume most of the 15-minute budget on polling alone.

Budget planning

Given the constraints (50/15min, 1000/24hr), plan your integration's request allocation:

Integration patternTypical budget consumptionGuidance
Real-time dashboardHigh burst, low dailyImplement caching. Re-query only on user action, not on interval.
Scheduled sync (hourly)Low burst, moderate daily24 syncs × ~20 requests = 480/day. Leaves room for other integrations.
Bulk data exportHigh burst, high dailyUse Jobs for any query touching >30 portfolios. Jobs consume 1 request to submit + exempt polling.
Event-driven webhook consumerUnpredictable burstBuffer incoming events and batch API calls. Never fan-out 1:1.

If multiple integrations share a firm, allocate budget explicitly. A runaway sync that consumes the full 15-minute window blocks every other integration at that firm for the remainder of the window.

Requesting higher limits

The default limits apply to all firms. If your integration requirements exceed 1,000 requests per day, contact Addepar Support with:

  • The integration's purpose and request pattern
  • Current daily request volume
  • Requested limit and justification

Log in to your Addepar account, click the question mark icon on the Global Navigation bar, and select Contact Support.


📘

Related

  • Response Codes - Full 429 error response format and troubleshooting
  • Jobs - Async pattern that minimizes rate limit consumption
  • Pagination - Control result set size to avoid timeouts

What’s Next

Did this page help you?