Technical
Debug APIs with Timestamps
personWritten by Magnus Silverstream
•calendar_todayFebruary 18, 2026
•schedule7 min read
You're staring at an API response that says exp: 1709913600. Is this token expired? What time does that even represent? Timestamps are everywhere in modern APIs — in authentication tokens, cache headers, rate limiting responses, and error logs. Knowing how to quickly decode and work with them turns hours of confusion into seconds of clarity.
Reading timestamps in API responses
APIs return timestamps in a variety of formats, and misreading them is one of the most common debugging mistakes.
Common formats you'll encounter:
• Unix seconds (10 digits): 1709913600 — used in JWT tokens, many REST APIs, webhook payloads
• Unix milliseconds (13 digits): 1709913600000 — used in JavaScript-based APIs, Elasticsearch, MongoDB
• ISO 8601: 2024-03-08T16:00:00Z — the most readable, used in well-documented APIs
• RFC 2822: Fri, 08 Mar 2024 16:00:00 GMT — used in HTTP headers (Date, Last-Modified)
Quick decode strategy:
• Count the digits first — this tells you the precision immediately
• If 10 digits, it's seconds since 1970
• If 13 digits, divide by 1000 before converting
• If it starts with a year (2024-), it's ISO 8601
• Use an online timestamp converter to instantly verify your reading
Common traps:
• An API documenting "timestamp" without specifying seconds or milliseconds
• Mixing UTC timestamps with local-timezone display in the same response
• Fractional seconds appearing as a decimal (1709913600.123) — these are seconds with millisecond precision
JWT token timestamps
JSON Web Tokens use timestamps extensively for security, and misunderstanding them leads to authentication bugs that are notoriously hard to track down.
Standard JWT timestamp claims:
• iat (Issued At): when the token was created — always in Unix seconds
• exp (Expiration Time): when the token becomes invalid — always in Unix seconds
• nbf (Not Before): the token is not valid before this time — always in Unix seconds
Debugging a JWT:
• Decode the payload (base64url) — tools like jwt.io or a text encoder can help
• Check exp against the current Unix timestamp: if exp < now, the token is expired
• Calculate remaining validity: exp - now = seconds until expiration
• Verify iat is in the past — a future iat indicates clock skew
Common JWT timestamp bugs:
• Server clock skew: the issuing server's clock is ahead or behind, causing tokens to appear expired on the validating server. Fix: allow a 30-60 second leeway in validation.
• Millisecond confusion: some libraries accidentally set exp in milliseconds instead of seconds. A 13-digit exp is a dead giveaway — the token would expire in the year 55,000+.
• Timezone assumption: developers subtract hours from exp thinking it's in local time. JWT timestamps are always UTC.
Debug command (bash):
• echo $JWT_PAYLOAD | base64 -d | jq '.exp' | xargs -I{} date -d @{}
HTTP Date headers and caching
HTTP responses carry several time-related headers that control caching, freshness, and conditional requests.
Key headers:
• Date: when the server generated the response — always in RFC 2822 format (e.g., Fri, 08 Mar 2024 16:00:00 GMT)
• Last-Modified: when the resource was last changed — used for conditional GET requests
• Expires: an absolute date after which the response is considered stale
• Cache-Control: max-age=3600 — relative freshness in seconds (not a timestamp, but time-related)
• Age: how many seconds the response has been sitting in a cache
Debugging cache issues:
• If a response seems stale, compare the Date header with your current time
• Calculate actual cache age: current_time - Date header value
• If Age header is present, the response came from a proxy cache
• max-age overrides Expires when both are present
Conditional requests:
• If-Modified-Since: send the Last-Modified value from a previous response
• The server returns 304 Not Modified if the resource hasn't changed, saving bandwidth
• Mismatch between server and client clocks can cause unnecessary full responses
Practical tip: use curl -v to see all response headers with timestamps, or check your browser DevTools Network tab and sort by timing columns.
Timezone debugging in APIs
Timezone mismatches are the source of some of the most frustrating API bugs — everything looks correct until users in a different timezone report wrong data.
Common scenarios:
• An API returns timestamps in the server's local timezone without indicating which timezone
• A client converts a UTC timestamp to local time twice, shifting it by the offset amount
• A database stores timestamps without timezone info, and different application servers interpret them differently
Debugging steps:
• First, determine what timezone the API actually returns — send a request and compare the timestamp to the current UTC time
• Check if the offset matches any common timezone (e.g., -5 hours = EST, +1 hour = CET)
• Look for the presence or absence of a Z suffix or offset in ISO 8601 timestamps: 2024-03-08T16:00:00Z (UTC) vs 2024-03-08T11:00:00-05:00 (EST) vs 2024-03-08T16:00:00 (ambiguous)
Red flags in API responses:
• Timestamps without timezone indicator — these are almost always bugs waiting to happen
• Timestamps that shift by exactly 1 hour twice a year — this means the server uses local time and is affected by DST
• created_at and updated_at in different timezones within the same response — indicates inconsistent server configuration
The fix:
• Always request timestamps in UTC from the API
• Convert to local time only in the frontend, as close to the display layer as possible
• Use libraries that handle timezone conversion correctly (Intl.DateTimeFormat in JavaScript, pytz or zoneinfo in Python)
Rate limiting and retry headers
Rate-limited APIs use timestamps to tell you when you can retry, and misreading them means either waiting too long or hammering the server unnecessarily.
Common rate limit headers:
• X-RateLimit-Reset: a Unix timestamp indicating when the rate limit window resets
• Retry-After: either a number of seconds to wait OR an HTTP date (RFC 2822)
• X-RateLimit-Remaining: number of requests left in the current window
• X-RateLimit-Limit: total requests allowed per window
Debugging rate limits:
• Check X-RateLimit-Reset — if it's 10 digits, it's a Unix timestamp. Subtract the current time to get seconds until reset.
• If Retry-After is a number (e.g., 30), wait that many seconds
• If Retry-After is a date string, parse it and calculate the difference from now
Common mistakes:
• Treating X-RateLimit-Reset as seconds-to-wait instead of an absolute timestamp — this causes either immediate retries (reset is a future timestamp like 1709917200) or extremely long waits
• Not checking whether Retry-After is a number or a date — the same header can use either format per the HTTP spec
• Ignoring rate limits during development, then hitting them in production when traffic scales
Robust retry logic:
• Parse the reset timestamp and calculate wait time dynamically
• Add a small jitter (random 1-3 seconds) to avoid thundering herd when many clients reset simultaneously
• Log the raw header values so you can debug without re-triggering the limit
Timestamps in logging and tracing
Log timestamps are your primary tool for reconstructing what happened and when, especially in distributed systems.
Log timestamp formats:
• Structured logs (JSON): usually ISO 8601 with millisecond precision — "timestamp": "2024-03-08T16:00:00.123Z"
• Syslog: RFC 3339 or the older BSD format — Mar 8 16:00:00
• Application logs: varies by framework — some use local time, others use UTC
• Cloud provider logs (AWS CloudWatch, GCP Logging): usually Unix milliseconds or ISO 8601 UTC
Correlating events across services:
• All services must use synchronized clocks (NTP) — even 1-second drift can reorder events incorrectly
• Use trace IDs alongside timestamps to group related events across services
• Sort by timestamp first, then by sequence number if available
Debugging with timestamps:
• Filter logs to a narrow time window around the reported incident
• Convert all timestamps to the same format (UTC) before comparing
• Watch for gaps — a 5-second gap between the request log and the response log might indicate a slow database query or network timeout
• Check for clock jumps — NTP corrections can cause timestamps to go backward
Practical tip: pipe log timestamps through a converter tool to make them human-readable. Reading 1709913600123 in a log is much harder than seeing 2024-03-08T16:00:00.123Z.
Conclusion
Timestamps are the thread that ties together every component of a modern application — from the JWT that grants access to the cache header that speeds delivery to the log entry that diagnoses failures. Developing fluency in reading and converting timestamps across formats is one of the highest-leverage debugging skills you can build. Use our timestamp converter to instantly decode any timestamp format you encounter during your next debugging session.
Frequently Asked Questions
Count the digits. A 10-digit number is in seconds (e.g., 1709913600 represents a date in 2024). A 13-digit number is in milliseconds (e.g., 1709913600000). If you convert a 13-digit number as seconds, you'll get a date thousands of years in the future — a clear sign you need to divide by 1000.