Rate limit headers
Every API response includes three headers that report your current limit status:| Header | Type | Description |
|---|---|---|
X-RateLimit-Limit | integer | Total request limit for the current window (always 5000). |
X-RateLimit-Remaining | integer | Number of requests remaining in the current window. |
X-RateLimit-Reset | integer | Unix timestamp (seconds) when the current window resets and X-RateLimit-Remaining returns to 5000. |
X-RateLimit-Remaining during high-volume operations so you can slow down before hitting the limit rather than recovering from a 429.
When you hit the limit
When you exceed 5,000 requests in the current hour, the API returns429 Too Many Requests.
A 429 response includes a Retry-After header indicating how many seconds to wait before retrying:
Handling 429 with exponential backoff
When you receive a429, respect the Retry-After header and wait the indicated number of seconds before retrying. For high-volume integrations, add exponential backoff with jitter on top of the Retry-After value to avoid a burst of synchronized retries if multiple workers hit the limit simultaneously.
Staying within limits
Use bulk and query endpoints. When you need to fetch or update multiple records, use bulk endpoints —POST /api/Contact/Query for Contacts, POST /api/Gift/Query for Gifts, POST /api/Gift/Bulk for bulk gift writes — rather than calling GET /api/Contact/{id} in a loop. Bulk endpoints return many records per request and dramatically reduce your request count.
Use webhooks for change detection. Polling for new or updated records burns requests quickly. Subscribe to webhooks instead — Virtuous pushes change events to your endpoint in real time, and webhook deliveries do not count against your hourly limit. See Webhooks Overview.
Avoid high-frequency polling. Do not poll the API more than once per minute for any given resource. If you need near-real-time updates, use webhooks.
Spread batch jobs across the hour. If you have a sync job that needs to make 4,000+ requests, spread it over multiple rate-limit windows rather than running all requests in a single burst. This leaves headroom for webhook deliveries and UI-triggered requests that share the same rate limit.
Monitor remaining capacity. Read X-RateLimit-Remaining from each response and reduce your request rate as you approach zero. Do not wait for a 429 to start slowing down.
Rate limit scope
The 5,000-requests-per-hour limit is enforced at the Virtuous organization level. Every API Key and every OAuth token issued inside an organization shares the same hourly bucket. TheX-RateLimit-Remaining header reflects the remaining capacity for the entire organization — not for the specific credential making the call.
This has three implications for partner integrations:
- A nonprofit’s total request budget is shared across all integrations they run. If a customer uses your integration plus three other tools that call CRM+, all four integrations draw from the same 5,000/hour bucket on that organization. Plan your integration’s typical request rate well below the full limit to leave headroom for the customer’s other callers.
- Multiple credentials in your integration don’t expand the budget. Generating a second API Key inside the same organization gives you a second credential, not a second rate-limit bucket. The two keys share the limit. The right reason to use multiple keys is permission-group separation or credential isolation per environment — not capacity scaling.
- Each nonprofit customer has its own independent budget. Because each customer is a separate Virtuous organization with separate credentials, a burst in one customer’s sync does not consume budget from another customer’s calls. This is what makes a partner integration serving many customers viable — the 5,000/hour limit applies per customer, not across your entire customer base.
Requesting a higher limit
The Virtuous engineering team can grant per-organization rate limit exceptions for legitimate high-volume use cases — for example, an initial historical-data migration or a partner with consistent high-throughput requirements. Exceptions are handled case-by-case and typically scoped to a specific organization or source IP. If your integration’s expected request rate exceeds the default budget for a specific customer, route the customer through Virtuous support to discuss an exception.Cross-API note
The Raise API and Volunteer API enforce their own rate limits independently of CRM+. A429 on one product does not affect your ability to call the others. If your integration spans multiple products, track remaining capacity per product separately.
Next steps
Error Handling
The error response shape and how to branch on status codes — including the retryable vs. non-retryable matrix.
Pagination and Filtering
Use
take=1000 and filtered queries to minimize requests when fetching large result sets.Webhooks Overview
Replace polling with real-time webhook deliveries — they do not count against the rate limit.
API Performance Tips
Patterns for keeping latency low and request counts down at scale.