Best Practices
Six areas every API integration should get right
1
Rate Limiting
Understand per-tier limits, monitor quota headers, and implement exponential backoff with jitter.
Performance2
Token Management
Cache tokens, refresh proactively 60 s before expiry, and use thread-safe storage in concurrent apps.
Reliability3
Caching
Match cache TTL to data freshness - from 24 h for static data to 15 s for real-time status.
Performance4
Error Handling
Handle every HTTP status code and design with circuit breakers and graceful fallbacks.
Reliability5
Security
Store secrets in a vault, use per-environment credentials, and rotate regularly.
Security6
Testing
ACC access is not automatic - even Partner accounts must request it. Contact the API team to get access.
QualityBuilding robust integrations with Schiphol APIs requires following patterns that ensure reliability, performance, and a smooth experience for your users. This guide covers the essential practices every developer should apply - whether you are building a new integration from scratch or hardening an existing one.
1. Rate Limiting
All Schiphol APIs enforce rate limits per application to ensure fair usage and platform stability. Exceeding a limit returns a 429 Too Many Requests response with a Retry-After header indicating how long to wait.
| Tier | Rate Limit | Notes |
|---|---|---|
| Public | 100 requests / minute | Suitable for display and informational applications |
| Partner | 1,000 requests / minute | For commercial and integration partners |
| Internal | 10,000 requests / minute | Schiphol internal systems only |
2. Token Management
OAuth 2.0 access tokens are valid for 30 minutes. Mismanaging tokens is the most common cause of 401 Unauthorized errors in production.
1
Request a token once - then cache and reuse it
Never request a new token for every API call. A single token is valid for 30 minutes and should serve all requests within that window. Requesting a new token per call wastes Auth0 quota and adds 100-200 ms latency to every request.
2
Refresh proactively - 60 seconds before expiry
Read expires_in from the token response (usually 1800 s = 30 min). Schedule a background refresh 60 s before the token expires. This ensures you always have a valid token ready and avoids in-flight requests receiving a 401.
3
Use thread-safe storage in concurrent applications
In multi-threaded or async apps, protect token state with a mutex or lock. Without synchronisation, multiple threads may all detect token expiry simultaneously and each request a new token - this is the "token stampede" problem and will hit Auth0 rate limits.
4
Store Client Secret in a secrets manager
Never hardcode Client ID or Client Secret in source code or config files. Use Azure Key Vault, HashiCorp Vault, or environment variables. Rotate secrets at least every 90 days and immediately if a secret is exposed.
Token stampede warning
In high-concurrency systems, if the token expires and 50+ threads all detect it simultaneously, they may all try to refresh at once. Use a single background token-refresh worker and share the token reference with all threads via a read-write lock.
3. Caching Strategies
Caching is the single most effective way to reduce API call volume, lower latency, and stay within rate limits. Different data types have different freshness requirements - match your TTL to the data, not to a single global setting.
| Data Type | Recommended TTL | Notes |
|---|---|---|
| Static reference data - airlines, destinations, aircraft types | 24 hours | Changes very rarely; safe to cache aggressively |
| Flight schedules | 5-15 minutes | Changes a few times per day; short TTL is sufficient |
| Flight status - gate, delay, boarding | 30-60 seconds | Time-sensitive; keep cache short |
| Real-time arrivals / departures | 15-30 seconds | Even a 15 s cache dramatically cuts repeated identical calls |
| User-specific / subscription data | Do not cache | Never share personalised data across callers |
Application-level vs. per-user caches
Flight schedule and status data is the same for all users - cache it at the application level and serve all users from a single cache entry. Never cache data that contains user-specific subscription or authorisation information in a shared cache.
Stale-while-revalidate pattern
For non-critical data, serve the stale cached version immediately while refreshing in the background. This eliminates cache-miss latency spikes at TTL boundaries. Suitable for flight schedules and reference data - do not use for real-time status data where freshness is critical.
4. Error Handling
Robust error handling ensures your application degrades gracefully rather than failing hard when the API returns an unexpected response. Design every integration to handle failure as a first-class concern.
| Status | Meaning | Recommended Action |
|---|---|---|
200-299 | Success | Process the response normally |
304 | Not Modified | Use your cached copy - nothing has changed |
400 | Bad Request | Fix the request (invalid parameter, missing field) - do not retry as-is |
401 | Unauthorized | Refresh the access token and retry the request once |
403 | Forbidden | Check subscription status - approval may be pending; contact the API team |
404 | Not Found | Validate the flight number, ID, or resource path |
429 | Too Many Requests | Back off exponentially; always honour the Retry-After header |
500 | Server Error | Retry with exponential backoff - do not try to fix in the client |
503 | Service Unavailable | The service is temporarily down; retry after the Retry-After period |
Design for failure from the start
Treat every external API call as potentially failing. Use timeouts (5 s connect / 10 s read), circuit breakers to stop calling a failing API, and fallback values so a temporary API issue does not cascade into a full application outage.
Which errors are safe to retry?
Safe to retry (with backoff): 429, 500, 502, 503, 504, and network timeouts.
Do not retry: 400 (fix your request first), 401 (refresh token then retry once), 403 (subscription issue - retrying won't help), 404 (resource does not exist).
5. API Versioning
Always pin your integration to a specific API version. Calling an unversioned endpoint may result in silent breaking changes when we release a new major version.
- Specify the version in the URL path:
/public-flights/v4/flights - Subscribe to the News & Updates page for version announcements and deprecation notices
- If you have ACC access, test against the new version there before migrating production
- Allow at least 2 weeks of parallel testing before switching your production integration
Version support policy
We support each major API version for at least 18 months after the next version is released. Deprecation notices are published at least 6 months before end-of-life. You will never wake up to a surprise breaking change.
6. Security
Credential exposure is the most common security incident in API integrations. Follow these rules without exception.
Do
- Store Client ID & Secret in a secrets manager (Azure Key Vault, HashiCorp Vault)
- Use separate credentials per environment (dev, test, production)
- Rotate secrets at least every 90 days
- Revoke and reissue immediately if a secret is exposed
- Validate and sanitise all API response data before using it
Don't
- Hardcode secrets in source code or config files
- Commit secrets to version control - even private repos
- Expose the Client Secret in client-side JavaScript or mobile apps
- Share one credential set across multiple teams or environments
- Log access tokens or secrets in application logs or error messages
7. Performance Tips
Small optimisations compound. Use the following patterns to get the most performance out of every API call.
Use pagination and field filtering
Most list endpoints support page and pageSize parameters. Always paginate large result sets - fetching everything in one call is wasteful and slow. Where the API supports field selection, request only the fields your application uses. Smaller payloads are faster and cheaper on bandwidth.
GET /public/public-flights/v4/flights?page=0&pageSize=20
Set connection and read timeouts
Always configure explicit timeouts on your HTTP client. Without them, a slow or hung response will block a thread indefinitely and exhaust your thread pool.
Recommended: 5 s connection timeout · 10 s read timeout. If your use case requires longer timeouts (e.g. large schedule downloads), set them explicitly and log slow responses for monitoring.
Reuse HTTP connections (Keep-Alive / connection pooling)
Establishing a new TLS connection for every request adds 100-300 ms of overhead. Use an HTTP client that supports keep-alive and connection pooling. Most popular libraries handle this automatically when you reuse the client instance:
- Python: use a
requests.Session()object - Java: use
WebClientor a sharedOkHttpClient - Node.js:
axiosandnode-fetchreuse connections by default
8. Testing & Environments
The ACC (Acceptance) environment mirrors the production configuration and is the best place to validate your integration before going live. ACC access is not automatic - it is not available to Public tier users and is not guaranteed for all Partner accounts either. You can request access and the Schiphol API team will review your situation.
Requesting ACC access
ACC is not available to Public tier users and is not automatically granted to all Partner accounts. If you need a test environment, contact the Schiphol API team with your use case and we will review your request. Internal tier users have ACC access by default.
ACC API basehttps://api-acc.schiphol.nl
ACC token endpointhttps://api-acc.auth.schiphol.nl/oauth/token
ACC Developer Portalhttps://developer-acc.schiphol.nl
PRD API basehttps://api.schiphol.nl
PRD token endpointhttps://api.auth.schiphol.nl/oauth/token
Never use production credentials in ACC
If you have ACC access, always create a separate application in the ACC Developer Portal with its own Client ID and Secret. Never reuse production credentials in a test environment.
What to test before going to production
Run the following checklist before going live - against ACC if you have access, or with careful low-volume testing against production:
- Token acquisition and automatic refresh works correctly
- All expected HTTP status codes (200, 401, 403, 429, 500) are handled
- Exponential backoff fires correctly on 429 and 5xx responses
- Responses are parsed correctly for edge cases (empty arrays, null fields)
- Timeouts are configured and do not hang the application
- Caching returns fresh data and does not serve stale responses indefinitely
- No secrets or tokens appear in application logs