Unix Timestamps in REST APIs
Updated: May 2026
There is no universal standard for how REST APIs represent time. Some use Unix seconds, others use ISO 8601 strings, and some use milliseconds. Knowing which convention a specific API follows — and how to translate between them — saves hours of debugging when integrating third-party services.
Free · No upload · Instant
Seconds vs ISO 8601 — the two camps
Most major APIs fall into one of two camps:
- Unix seconds integers — Stripe, Twilio, SendGrid, most financial APIs. Compact, sortable, unambiguous, easy to compare and do arithmetic with. The unit is always seconds.
- ISO 8601 strings — GitHub, Google, AWS, Salesforce, most modern REST APIs. Human-readable, self-describing, includes timezone offset. Format:
2025-01-01T00:00:00Z.
When consuming an API, always check the documentation for the unit. When designing your own API, pick one convention and document it clearly in every field description. Mixed conventions in the same API are a common source of client-side bugs.
Stripe — pure Unix seconds
Stripe is the canonical example of the Unix-seconds convention. Every date field in the Stripe API is an integer count of seconds since the Unix epoch, with no exceptions.
// Stripe charge object (simplified)
{
"id": "ch_3QxyzABC123",
"created": 1735689600, // Unix seconds
"amount": 4999,
"currency": "usd",
"customer": "cus_ABC123"
}
// Convert in JavaScript
const created = new Date(charge.created * 1000).toISOString();
// → "2025-01-01T00:00:00.000Z"
// Filter by date in a Stripe list
const charges = await stripe.charges.list({
created: {
gte: 1735689600, // Jan 1, 2025 00:00:00 UTC
lt: 1767225600 // Jan 1, 2026 00:00:00 UTC
}
});
Rate-limit headers — often Unix timestamps
Many APIs communicate rate limit reset times as Unix timestamps in response headers. The standard X-RateLimit-Reset header (GitHub, Twitter, and others) contains a Unix timestamp in seconds indicating when the rate limit window resets.
// Rate limit headers example
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1735693200 // Unix seconds
// Convert to human time in JavaScript
const resetTs = parseInt(response.headers['x-ratelimit-reset']);
const resetDate = new Date(resetTs * 1000);
const waitMs = resetDate - Date.now();
console.log(`Rate limited. Resets at ${resetDate.toISOString()}`);
console.log(`Wait ${Math.ceil(waitMs / 1000)} seconds`);
Cursor-based pagination with timestamps
Many APIs use Unix timestamps as pagination cursors. Instead of page numbers, you pass the timestamp of the last item received to get the next page. This is stable even when items are inserted concurrently.
// Slack conversations.history uses Unix timestamp cursors
// First page — most recent 100 messages
GET /api/conversations.history?channel=C123&limit=100
// Response contains:
{
"messages": [...],
"has_more": true,
"response_metadata": {
"next_cursor": "bmV4dF90czoxNzM1Njg5NjAw" // base64
}
}
// Some APIs use raw timestamp as cursor (e.g. oldest/latest)
GET /api/conversations.history?channel=C123&oldest=1735689600&latest=1735693200
// Twitter/X uses since_id and max_id for tweet IDs
// but older search APIs used Unix timestamps directly
Webhook timestamps and replay protection
Webhooks from Stripe, GitHub and other services include a timestamp in the signature header to prevent replay attacks. The receiving server should reject webhooks with a timestamp more than 5 minutes old.
// Stripe webhook signature header
Stripe-Signature: t=1735689600,v1=abc123...
// Extract and validate
function validateStripeWebhook(payload, header, secret) {
const parts = header.split(',');
const ts = parseInt(parts[0].split('=')[1]);
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - ts) > 300) {
throw new Error('Webhook timestamp too old — possible replay attack');
}
// ... verify HMAC signature
}
When debugging webhook delivery issues, paste the timestamp from the header into the Flowfiles converter to verify it matches the expected delivery time and is within the 5-minute validation window.
API design recommendations
- Choose one format and apply it everywhere. If you use Unix seconds for one endpoint, use it for all endpoints. Mixed conventions are the biggest source of client confusion.
- Document the unit in the field name or description. Names like
created_atare ambiguous.created_at_sor a schema annotation ("format": "unix-timestamp-seconds") removes all doubt. - Accept ISO 8601 on input. Many clients find it easier to send
"2025-01-01T00:00:00Z"than to compute a Unix timestamp. Accept both and normalise internally. - Return both for important fields. Some APIs return both
"created": 1735689600and"created_iso": "2025-01-01T00:00:00Z"— convenient for debugging without increasing payload size significantly. - Use 64-bit integers. Future-proof your API by specifying
int64orbigintin your schema definitions, notint32.