# Designing a public API: the decisions behind our launch

> Why we chose REST over GraphQL, headers over query strings, and rolling quotas over calendar months.

**Author:** Camila Ribeiro — Field Operations Editor  
**Published:** 2026-05-02  
**Updated:** 2026-05-02  
**URL:** https://quilometragem.com/blog/designing-a-public-api-the-decisions-behind-our-launch

**TL;DR:** Why we chose REST over GraphQL, headers over query strings, and rolling quotas over calendar months.

- Our API is consumed by HR and fintech developers who typically integrate five or six APIs per project.
- API keys passed in query strings end up in server logs, browser referers, and screenshots.
- We chose rolling 30 days over calendar months because the calendar creates artificial spikes at the start of each month.
- 1,000 requests per window comfortably covers a prototype, a hackathon, or a 10-person team testing an integration.
- The POST /receipt endpoint returns a SHA-256 of the input fields.

## The audience drives the style

Our API is consumed by HR and fintech developers who typically integrate five or six APIs per project.[^irs-2025] They don't want to learn a new query language or wire up resolvers — they want cURL, fetch, and requests. So we went with classic REST: five endpoints, plain JSON, OpenAPI 3.1. For our use case (calculate distance and reimbursement), GraphQL would add nothing. It would only introduce another client and a learning curve.

## Auth in the header, not in the URL

API keys passed in query strings end up in server logs, browser referers, and screenshots. Moving them to `X-API-Key` (with `Authorization: Bearer …` as a fallback) removes most of that leakage without complicating the developer's life — every HTTP client supports first-class headers.

## Rolling 30-day quotas, not calendar months

We chose rolling 30 days over calendar months because the calendar creates artificial spikes at the start of each month. With a rolling window, the limit distributes naturally. The window resets when the oldest request falls outside the last 30 days — a simple implementation in the database with a `usage_window_start` plus counter.

## A free tier large enough to prove value

1,000 requests per window comfortably covers a prototype, a hackathon, or a 10-person team testing an integration. Beyond that we expect a human conversation — anyone consuming 5,000+ req/month usually already has SLA, IP-allowlist, or key-rotation requirements that deserve a tailored partner plan.

## An integrity hash on every receipt

The `POST /receipt` endpoint returns a SHA-256 of the input fields. Auditors can recompute the hash locally and compare — any alteration to the PDF is instantly detectable. It's the same technique banks use for immutable statements.

## OpenAPI before code

We designed the OpenAPI spec first, generated TypeScript types from it, and only then wrote the handlers. That guarantees the documentation never drifts — it's the contract.

## What we left out (for now)

Webhooks, batch endpoints, and multi-stop route optimization. Each has real demand but adds complexity. We'll ship them as partners request — and every release will land in the [public changelog](/api/changelog) with an RSS feed so nobody has to ask us.

## Sources

- [IRS — Standard Mileage Rates for 2025](https://www.irs.gov/tax-professionals/standard-mileage-rates) — Internal Revenue Service (2026-04-28)
