OCPI doesn’t define rate limits. That’s left to each implementer. Which means that in production, every OCPI endpoint needs its own rate-limiting layer, and every OCPI client needs to handle being rate-limited gracefully.
Without rate limiting, a single misbehaving partner can DoS your endpoint. Without graceful rate-limit handling, your client can DoS partners and get blocked. Neither is the OCPI spec’s fault — they’re operational realities the spec leaves you to handle. If you’re still getting oriented on the protocol itself, start with what OCPI is before diving into these operational patterns.
This article covers the rate-limiting patterns that actually work in production, both server-side (protecting your endpoints) and client-side (being a good citizen with partners).
Why rate limiting matters
A list of real OCPI scenarios where rate limiting saves the day.
Runaway sync loop. A partner’s code has a bug that pulls your Locations endpoint in a tight loop. Without rate limiting, your endpoint is overwhelmed; legitimate traffic suffers.
Misconfigured polling. A partner intends to poll daily but configures hourly by accident. Your endpoint sees 24x expected load.
New partner ramp-up. A new partner integrates your endpoint and accidentally triggers full re-sync repeatedly during testing. Tanks your endpoint during their iteration.
Outage cascade. Your downstream system has a hiccup; your responses slow. Partners’ code retries aggressively. Now you have your original slowness plus 5x retry traffic. This retry-storm behavior is one of the recurring OCPI integration pitfalls (coming soon) that only surfaces under real load.
Bad actor. Compromised or abusive partner attempts to scrape your data faster than the partnership allows.
Without rate limiting, each of these can take down your endpoints. With rate limiting, they’re contained.
Server-side: protecting your endpoints
A few patterns that work.
Per-partner sliding window
Track requests per partner per time window. Reject (with 429) when limit exceeded.
Example: 60 requests per minute per partner for Locations endpoint.
Implementation: Redis or in-memory counter with timestamp expiry. Increment on each request; check before processing.
Pros: simple, well-understood pattern, partner-specific.
Cons: doesn’t protect against bursts within window.
Per-partner token bucket
Each partner has a token bucket that refills at a steady rate. Each request consumes a token. Empty bucket → reject.
Example: 60 tokens, refill at 1 token/second, max bucket 60.
Pros: allows short bursts up to bucket size, smooths over time.
Cons: slightly more complex to implement.
Global rate limiting
System-wide limit regardless of partner. Protects against the case where many partners simultaneously misbehave.
Pros: backstop protection.
Cons: legitimate partners can be affected by other partners’ misbehavior.
Endpoint-specific limits
Different endpoints have different costs. Locations bulk-pull is expensive; a single Token query is cheap. Apply different limits per endpoint.
Tiered limits
Different partners get different limits based on their tier (free, basic, premium, strategic). The contract defines tier.
The 429 response
When you rate-limit, respond with HTTP 429:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{
"status_code": 2003,
"status_message": "Rate limit exceeded. Retry after 60 seconds."
}
Key elements:
- HTTP 429 status.
- Retry-After header telling the client when to retry (seconds or HTTP-date).
- Body with OCPI StatusCode for programmatic handling.
Some implementations use HTTP 503 (Service Unavailable) instead. 429 is more semantically correct for rate limiting; 503 is generic “we can’t right now.”
Client-side: being a good partner
If you’re consuming OCPI endpoints, design for rate limits. The core client decision on every response looks like this:
flowchart TD
A[Send OCPI request] --> B{Status 429?}
B -->|No| C[Process response]
B -->|Yes| D{Retry-After<br/>present?}
D -->|Yes| E[Wait Retry-After]
D -->|No| F[Backoff + jitter]
E --> G[Retry request]
F --> G
G --> B
style D fill:#e8f0fe,stroke:#4285f4
Respect Retry-After
When you get 429, honor the Retry-After header. Don’t immediately retry. Don’t ignore the header.
Exponential backoff with jitter
If you don’t have Retry-After, back off exponentially with jitter:
- First retry: 1-2 seconds.
- Second: 2-4 seconds.
- Third: 4-8 seconds.
- Capped at some max (60-300 seconds).
Without jitter, all your retries can sync up and hammer the server at the same moments.
Batch where possible
Instead of one request per item, batch into one request for many items where the API supports it. Pagination on GETs, bulk PUTs on Locations, etc.
Cache aggressively
Don’t re-fetch data you already have. Locations data doesn’t change frequently; cache and refresh on a schedule, not per-query.
Smart polling intervals
Polling daily? Hourly? Per-minute? Match the actual data change frequency. Locations change rarely; polling more than daily is wasted load.
Background processing
Don’t block your user-facing operations on potentially-rate-limited calls. Queue and process async.
Communicating limits to partners
Server side: communicate your limits.
Documentation. “Our OCPI endpoints rate-limit at X requests per minute per partner.” Public or partner-portal documentation.
Bilateral discussions. When onboarding a new partner, agree on expected traffic patterns and confirm they fit within limits.
Custom limits per agreement. Strategic partners may have negotiated higher limits.
Headers. Some implementations return X-RateLimit-Remaining or similar headers so partners can see their current consumption. Optional but appreciated.
Alerts to partners. If a partner is approaching their limit, email or notify them proactively. Better than waiting for 429s to start firing.
Common implementation pitfalls
A list of things that go wrong.
Rate limits too low. Legitimate partners hit 429s constantly. Loosen the limits or partner experience suffers.
Rate limits too high. Doesn’t protect against abuse. Tune based on actual load.
No Retry-After header. Partners have to guess. Better to specify.
Inconsistent enforcement. Some endpoints enforce limits, others don’t. Confusing for partners.
Counting across multiple servers. If your API is load-balanced across N servers, per-server counters allow N times the intended rate. Use centralized counters (Redis) or proportional per-server allocation.
Failure mode handling. What happens if your rate-limiting infrastructure fails? Fail-open (allow all traffic, accept the risk) or fail-closed (block all traffic, severe but safe)? Decide and document.
Hot partner problem. One partner generates 80% of your traffic; their rate limit issue affects them but not your overall capacity. Per-partner limits work but you might want extra headroom for them.
Abuse-specific protection
Beyond accidental over-traffic, some patterns address intentional abuse.
Anomaly detection. Sudden change in partner’s request patterns (10x normal volume from one day to next) may indicate abuse or bug. Alert.
Geographic anomalies. Requests from unexpected source IPs (if partner has a fixed range). Investigate.
Off-pattern requests. A partner who normally queries Locations suddenly starts hitting Sessions and CDRs aggressively. Suspicious.
Token rotation enforcement. Old, leaked tokens are common attack vectors. Force token rotation periodically. This sits alongside your broader OCPI security and authentication (coming soon) posture — rate limiting protects availability, credential hygiene protects access.
Selective IP allowlisting. For partners with stable IPs, allow only those IPs. Reduces attack surface.
These are above-and-beyond simple rate limiting and matter more for high-risk environments (financial-grade OCPI, sensitive markets).
OCPI-specific considerations
A few things OCPI-specific.
Push endpoints can be hit during partner outages. When their endpoint comes back, queued pushes flood in. Rate limit but also accept the spike if recovery makes sense.
Pull endpoints during partner sync. A new integration or full re-sync legitimately generates a burst. Allow with limits.
Sessions endpoint during traffic spike. Real charging traffic can spike (holiday weekends, rate-promotion events). Don’t rate-limit legitimate session updates.
CDR endpoint criticality. CDRs are billing records. Rate limiting that drops them silently is bad — they need delivery guarantees. Prefer queuing and slow-but-deliver over reject.
Tiered limits by relationship
A common pattern: different limits by partnership type.
Strategic partner (large, direct, important): generous limits. Possibly no per-request limits and only volume-based monitoring.
Standard partner (typical roaming): moderate limits. Per-endpoint, per-minute.
Trial / new partner: conservative limits during initial integration. Increase as trust is established.
Hub-mediated traffic: the hub aggregates many parties; your limit should be for hub-level traffic, not per-end-party. The visibility you have here depends heavily on whether you connect via a hub or peer-to-peer.
This tiering is contractual as much as technical. Negotiate at the partnership level.
The honest summary
OCPI rate limiting is essential operational discipline that the spec leaves up to you. Implement per-partner limits with Retry-After responses. Document your limits. Tier by partnership. Build clients that respect 429s gracefully. The result is a stable system where misbehaving partners don’t take down your operations and where you’re not the partner who takes down others.