Skip to main content

Best practices

Non-binding recommendations that will make your integration faster, cheaper, and easier to operate. None of these are required — the API works without them — but every one of them has come up in real customer support tickets.

Networking & retries

Always set a request timeout

A hung request is worse than a failed one. Set explicit connect + read timeouts on every API call.

Python
requests.get(url, headers=headers, timeout=(5, 30)) # 5s connect, 30s read
JavaScript
const controller = new AbortController();
setTimeout(() => controller.abort(), 30_000);
fetch(url, {headers, signal: controller.signal});

Retry transient failures with backoff + jitter

Retry on 429, 500, 502, 503, 504. Don't retry 400, 401, 403, 404 — they fail the same way next time.

Combine exponential backoff with random jitter so parallel workers don't all retry at the same instant:

import random, time

def backoff(attempt: int) -> float:
return min(60, (2 ** attempt) + random.uniform(0, 1))

Cap at ~5 attempts; surface the failure to your caller after that.

Honour Retry-After headers

When you receive a 429, the response includes Retry-After (seconds). Use it as the wait time instead of your own backoff for that attempt. See Rate limits.


Pagination & data volume

Use the biggest page size you can

One request returning 1,000 records is roughly ten times cheaper than ten requests returning 100. Network round-trip dominates per-call cost.

Narrow with filters before paginating

A date range, site list, or record-type filter applied server-side is always cheaper than client-side filtering after the fact.

Don't fetch what you don't need

If you only display serial + last_login_time, don't pull the full device record. (The API reference lists exactly which fields each endpoint returns — use it to plan your requests.)


Caching

Cache anything that rarely changes.

Cache forResource
HoursSite list, site metadata, account metadata
MinutesPer-site configuration
Don't cacheLive measurement data, export status, command results

Conditional requests where supported

One endpoint — currently the batch-request status summary (GET /api/v1/devices/batch-requests/{batch_request_id}/) — supports ETag + If-None-Match conditional requests. The /results/ endpoint alongside it does not. When the resource hasn't changed since you last saw it, the server returns 304 Not Modified with no body, saving the bandwidth.

Conditional request
# First call — server returns 200 + ETag
curl -i -H "Authorization: Token ..." \
https://mytrafficdata.com/api/v1/devices/batch-requests/abc-123/
# ...response includes: ETag: "a1b2c3"

# Subsequent call — pass the ETag back
curl -i -H "Authorization: Token ..." \
-H 'If-None-Match: "a1b2c3"' \
https://mytrafficdata.com/api/v1/devices/batch-requests/abc-123/
# ...returns 304 Not Modified if unchanged

Most endpoints don't emit ETag yet. For those, use a client-side TTL or your platform's standard cache layer; invalidate on user-driven actions.


Idempotency on writes

For write operations that dispatch device commands or create resources, assume the network can drop the response before you see it. If a retry creates a duplicate, you have a problem.

The MyTrafficData API implements the Idempotency-Key HTTP header pattern (IETF draft) on selected write endpoints. In /api/v1/ that is:

EndpointIdempotent?
POST /api/v1/devices/dsd/flex/trigger/{id}/activate/✅ Yes
POST /api/v1/devices/dsd/flex/trigger/{id}/deactivate/✅ Yes
POST /api/v1/devices/dsd/by-serial/{int_serial}/flex/configure/lte/push/❌ No — use read-after-write

The endpoints that accept the header declare it as a parameter in the per-endpoint reference, so a generated client will expose it. Treat that reference as authoritative: sending the header to an endpoint that doesn't implement it is silently ignored, not rejected, so a retry will repeat the side effect.

How to use it on supported endpoints:

  1. Generate a UUID before the first attempt.
  2. Send it in the Idempotency-Key request header.
  3. Retry with the same key on network failures. The server detects the duplicate, returns the original response, and does not repeat the side effect.
  4. Use a new key for a logically new operation.
curl -X POST \
-H "Authorization: Token ..." \
-H "Idempotency-Key: 6b2f9d3a-3e3b-4a1f-9e8d-7c4d8f2a1e5d" \
-H "Content-Type: application/json" \
-d '{...}' \
https://mytrafficdata.com/api/v1/devices/dsd/flex/trigger/{id}/activate/

Notes:

  • Keys are scoped per endpoint — the same UUID on a different endpoint is treated as a new request.
  • Keys are remembered for 72 hours, then expire.
  • If you replay a key with a different payload, you get a 409 Conflict rather than the duplicated side effect.

For endpoints that don't accept Idempotency-Key — including the LTE push above — the read-after-write fallback works: after a successful POST, check the listing or status endpoint to confirm before retrying.


Token & secret hygiene

  • One token per integration. Never share a token across multiple deployments. Revoking one shouldn't take down everything.
  • Secret manager, not source control. Tokens have the same blast radius as passwords.
  • Rotate periodically. Every 6–12 months, or immediately after any suspected leak. See Authentication.
  • Audit usage. The MyTrafficData dashboard shows when your token was last used. Investigate unexpected gaps or spikes.

Time & timezones

  • Always use ISO 8601 strings in requests and responses (we always do; you should too).
  • Read the offset in each response value — don't assume a single timezone for the whole response. Different fields can carry different offsets. See Timezones.
  • Store timestamps as UTC in your own database if you ever do timezone math; convert to local for display only.

Error handling

  • Branch on the HTTP status code first, then inspect the body shape. v1 has three body shapes — see Errors.
  • Tolerate unknown fields in error bodies. We may add new fields (e.g., trace IDs, new error codes) without bumping the major version.
  • Surface human-readable messages (message, detail, or per-field arrays) directly to your end users — they're written to be safe.
  • Log the full response body on unexpected failures, plus the request URL, method, and timestamp. This shortens any support ticket dramatically.
  • Branch on error.code for domain errors (INVALID_ID, TIME_RANGE_EXCEEDED, etc.) — those codes are stable.

Operational hygiene

  • Monitor your own metrics — request count, latency p95, error rate by status code. We can't tell you why your integration broke at 3am; your metrics can.
  • Identify yourself. Set a meaningful User-Agent header (MyApp/1.4.2 (ops@example.com)) so we can correlate traffic if you contact support.
  • Test against a staging account if you have one — don't write integration tests that hit production data.

Summary checklist

  • ✅ Set timeouts on every request.
  • ✅ Retry transient errors with exponential backoff + jitter.
  • ✅ Honour Retry-After on 429s.
  • ✅ Page with bigger limit values; narrow with filters first.
  • ✅ Cache stable resources (site list, account metadata).
  • ✅ Use the Idempotency-Key header on supported endpoints.
  • ✅ One token per integration; rotate regularly.
  • ✅ Branch on HTTP status first; tolerate unknown fields.
  • ✅ Set a meaningful User-Agent.