Keyless APIs: Why Open Earth Data Should Not Need a Signup

Keyless APIs: Why Open Earth Data Should Not Need a Signup

Etzal Finance
By Etzal Finance
13 min read

Almost every geospatial API opens with the same sequence. Create an account, confirm an email address, generate a key, read a quota table, and only then send the first request and find out whether the data was any good. The step that decides everything, whether the response actually answers the question being asked, sits at the far end of the slowest part of the funnel.

That ordering is defensible when the data is expensive to produce or licensed per seat. It is much harder to defend when the underlying data is open, already public, and costs the operator a cache hit to serve.

The argument that follows is narrow. Keyless does not mean unlimited, and it does not mean unaccountable. It means the read surface answers an anonymous request, under an enforced limit, with a credential as an optional upgrade rather than a toll gate at the entrance.

What an API key actually does

Strip away habit and a key does three things. It identifies the caller, it meters what that caller consumed, and it enforces a limit on how fast they may consume it. Those three are bundled so routinely that they are rarely separated, but they have very different value.

Only the third protects the service. A limit is what stands between a loop with a bug in it and a saturated database, and it does not require identity. It requires a stable enough key to count against, and a network address serves for anonymous traffic. Identity makes the counting more precise, not more real.

Identification does buy one genuine capability: revoking a specific abuser without collateral damage. Blocking an address can catch an entire office or a carrier's shared pool, while blocking a key catches one account. But free self-serve keys are nearly as cheap to rotate as addresses, so that advantage is strongest exactly where keys are hard to obtain and the signup wall was already doing the work.

Metering mostly serves the operator. On an open surface with nothing to bill, it is analytics wearing a technical costume. The split that survives scrutiny: limit everyone, identify whoever volunteers, meter for whoever asked to be metered.

The four jobs people think a key does

Most key requirements are inherited rather than designed, so it is worth checking the four justifications that come up.

  • Abuse control. Partially real. An address-based bucket absorbs most of what actually happens, which is not attack traffic but a misconfigured client, a runaway retry loop, or someone scraping politely and far too fast. A determined attacker rotates addresses, and would rotate free keys just as easily.
  • Cost control. Real for endpoints that do work per call: model inference, routing over a large graph, anything that queues. Close to irrelevant for reads answered from cache. Applying one policy to both is how services end up rate limiting their cheapest operations hardest.
  • Attribution and analytics. Genuine value, but it accrues to the operator. Charging a prospective user a signup so the operator can build a funnel report is a poor trade before that user has decided the data is worth anything.
  • Lead capture. The honest reason behind a large share of key walls, and a legitimate business decision. It is not a technical requirement, and calling it one is where credibility is lost, because the engineers being courted are the people most likely to notice.

What the field actually does

The landscape is more varied than the default suggests, and the variation tracks who is paying for the data.

Commercial map and imagery providers gate everything, coherently. Their tiles, routing, and geocoding carry a real per-request cost, licensing is usually per seat, and terms often restrict caching and derivative works in ways that require knowing who is calling. The key is load bearing there.

Community infrastructure sits at the other end. Public geocoding and query endpoints over open map data commonly require no credential and are governed by etiquette instead: send a real user agent, keep the rate modest, do not run bulk extraction against an interactive endpoint, cache what you fetch. They are also chronically overloaded, for structural reasons. Consuming costs the caller nothing, serving costs the operator something, and nothing converts polite guidance into an actual refusal. A guideline that is never enforced is read by every automated client as the absence of a limit. The conclusion is not that keyless access fails. It is that keyless without limits fails, reliably, and then blames its users for it.

What keyless does not mean

Anonymous traffic still carries a ceiling, enforced per address, generous enough for evaluation and light production and tight enough that one bad client cannot degrade service for everyone else. A free key raises that ceiling substantially and attaches consumption to an account, so the caller sees their own numbers and the operator has something specific to revoke. Endpoints that trigger real cost keep tighter limits regardless of tier.

Keyless is also not the same as terms free. Open map data under a share-alike license still obliges attribution and still constrains what may be done with derived databases. An open API is a statement about access friction, not a waiver of the obligations attached to the bytes.

The distinction worth holding onto is between metering and gating. Metering counts what happened. Gating refuses to answer until conditions are satisfied. Open data deserves the first and rarely justifies the second.

Designing the two ceilings deliberately

A keyless tier is only credible if its numbers were chosen rather than copied from the previous project.

Start from the shape of a legitimate first session. Someone evaluating an API sends a handful of requests, reads the responses, changes a parameter, and tries again, often in a tight loop while a page reloads. A limit that interrupts that loop does not teach discipline, it teaches that the service is fragile, and the evaluator leaves with that impression rather than one of the data. A per minute ceiling in the low hundreds covers exploration and a modest production workload.

Then decide what the keyed tier buys. The gap has to be large enough to be worth the signup and small enough that the anonymous tier is not a demo. A factor of four or five moves serious users without making the open path feel crippled.

Split limits by cost class rather than running one global number. A cached feature read and a polygon query that scans a large table should not share a bucket, because one bucket forces the cheap operation down to the safe rate of the expensive one. Decide too what happens at the boundary: a smaller page, a coarser resolution, or a lower priority queue still answers the question, and a refusal does not.

Publish both ceilings somewhere machine readable, return the current limit state in response headers so a client can self regulate before it is refused, and on refusal say what was exceeded and when to retry. A bare status code with no explanation is how retry storms are manufactured.

Implementing the limiter without fooling yourself

The mechanics contain traps that are hard to notice from outside, because a broken limiter and a working one look identical until the day they do not. The first is the counting structure. A fixed window per minute is trivial to implement and lets a caller spend an entire allowance in the last second of one window and the whole next allowance in the first second of the following one, delivering twice the intended burst at the seam. A token bucket, refilling at a steady rate up to a capacity, smooths that and expresses burst tolerance separately from sustained rate. The fixed window is not wrong, it is wrong by a factor of two at the moment that matters most.

The second is identifying the caller behind a proxy. When traffic arrives through a CDN or load balancer, every request appears to come from the proxy, so the client address has to be read from a forwarded header. That header is trivially forged by anyone who can reach the origin directly. If the origin is publicly reachable, an attacker sends a fresh value on every request, lands in a fresh bucket every time, and the limiter becomes decorative while reporting healthy numbers. The fix is architectural rather than clever: ensure the origin accepts connections only from the proxy, and trust the forwarded value because of that guarantee rather than because of the header. Take the entry your own proxy appended rather than the leftmost value in the chain, since the leftmost is caller controlled. For IPv6, bucket on a network prefix, because one client can hold enough addresses to make per address limits meaningless.

The third is memory. A map keyed by caller grows with the number of distinct callers, and under a flood of spoofed identifiers it grows without bound, turning a courtesy limit into an outage. The limiter has to be bounded by construction: a hard cap on entries with eviction, or a fixed size array of counters that different callers may share. A limiter that occasionally lumps two strangers into one bucket is better than one that exhausts memory defending a free endpoint.

The edge cache changes the honest accounting

Most usage dashboards quietly get one detail wrong. When an API sits behind a CDN, a large share of requests never reach the origin at all, because the edge answers them from cache. If metering counts requests at the origin, those cached reads are invisible, and arguably they should be, because they cost almost nothing to serve. But the usage figure a caller sees is then not the number of requests they made. It is the number that reached the origin. A usage number that under reports is fine when the documentation explains why. One presented as complete when it is not is the sort of small dishonesty that makes every other claim on the page worth checking. The same effect distorts what a limit means: a caller whose requests land on popular, already cached resources is limited far more loosely than one sending the same volume against a cold and scattered key space, because only the misses reach the limiter.

A second consequence catches teams out. Per caller headers such as remaining quota are computed at the origin, so if a cacheable response carries them, the CDN will serve one caller's numbers to every other client hitting that URL for the life of the cache entry. Either strip those headers from cacheable responses, treat any credentialed request as uncacheable, or vary the cache key on the credential and accept the hit rate collapse. Serving a stale quota figure to the wrong client is worse than serving none, because a client that trusts it will back off for no reason, or fail to back off when it should.

Credentials do not belong in URLs

If a key is accepted as a query parameter for convenience, and most APIs eventually accept one because it makes a browser address bar work, it is worth remembering where URLs end up. They land in CDN cache keys, origin access logs, browser history, referrer headers sent to third parties, screenshots in bug reports, and the copy pasted command attached to a public issue.

The mitigations are unglamorous. Prefer a header for anything that grants elevated limits. When a query parameter is accepted, mark those requests uncacheable so the secret never becomes part of a shared cache key, and scope such keys to read only operations so a leak is bounded. And when documentation or a playground generates a copyable example, substitute a placeholder rather than the real key, since the fastest way to leak a credential is to make it effortless to share a working command.

Why agents make the case sharper

Autonomous callers turn a friction argument into a capability argument. An agent evaluating whether a data source can answer its question has no human standing by to receive a confirmation email and click through a console. If discovery requires credentials, the agent does not discover the service, and the operator never learns it was considered.

Keyless access alone is not sufficient for that audience. An agent also needs a machine readable description of what exists: the operations available, their parameters, what each returns, and how uncertainty and missing data are expressed. Fetch the manifest, choose the operation, call it, read a structured error if it fails, adjust, all without a human in the loop.

The forward path for the credential itself is delegation rather than agent accounts. Giving an agent its own account makes it a principal, which is the wrong shape: accountability has to sit with a person or organization that can be held to terms. The workable model is a scoped, revocable credential issued by that principal to a specific agent, with its own limits, its own permitted operations, and ideally its own budget. Revoking it removes one agent's access and nothing else, the property missing when a shared key is copied into every deployment. Keyless reads and scoped delegated keys are the same instinct at two levels of risk: grant the minimum needed to proceed, and make the grant easy to withdraw.

When a key is genuinely the right answer

Keyless is a default, not a doctrine. Require a credential when a request costs real money per call and the cost cannot be capped another way. Require one when the data carries per seat or per territory licensing that must be enforced to keep the upstream agreement intact. Require one when regulation obliges the operator to know who the customer is, or when paying users need guaranteed capacity during contention, since prioritizing anyone requires being able to tell them apart. None of those reasons are weakened by leaving the rest of the read surface open.

What does not survive examination is requiring a key because comparable APIs require one, and then calling it a security measure. That framing commits a team to defending a decision they never made on the merits, and it makes the necessary key walls elsewhere in the product look equally arbitrary.

The tradeoff being accepted

Keyless access has a real cost. Anonymous load cannot be attributed precisely, which makes capacity planning coarser, and support gets harder because a caller reporting a problem cannot always be correlated with the requests that caused it. An operator choosing this path is choosing to pay those costs.

The test that decides it is simple enough to apply to any endpoint. Name which of the four jobs the key is doing, then name what specifically breaks if the key is removed and the limit stays. If the answer is that costs run away, a license is violated, or paying customers lose capacity, keep the key and say so plainly. If there is no answer beyond the fact that the signup form already exists, the key is not protecting the service. It is collecting something, and the price of collecting it is every evaluation that ended before the first request was sent.