← all work

Geolink

A geolocation API platform for the Egyptian market — text search, directions, geocoding, credit-based billing. Four rewrites, in production since 2023.

Role
Founder, full-stack
Span
2023 — now
Kind
Own venture · platform
Status
live
Stack
Flask · SQLAlchemy · React · Docker · PostgreSQL · REST API
geolink-eg.com

The gap it fills

Egypt didn’t have a production-grade geolocation API built for the local market — text search, directions, geocoding, reverse geocoding, priced and billed for real usage. Geolink is that layer: Flask + SQLAlchemy underneath, a credit-based billing system on top, and an admin dashboard for the operational side.

Four rewrites to get here

The current platform is the fourth version. A private v1, then a public API, then a private v2, then the Flask rewrite that runs today. That’s the honest shape of it — the thing that’s been live since 2023 is the service, not this codebase, and each rewrite happened because the previous one couldn’t carry what the next year asked of it.

Running at real scale

~5 million requests a month, 50+ active clients — startups, real products, individual developers. That volume forced real engineering, not a demo API: five caching layers, connection pooling (20 persistent, 30 overflow, pre-ping on checkout so a stale connection fails at the pool instead of mid-request), and async queue-based usage logging. A unified dashboard endpoint replaced what used to be several round trips — one screen, one call, and it’s the screen every client actually looks at.

Billing can never block a request

Usage logging is queued and drained by a background worker, so charging an account, writing the ledger row and invalidating caches all happen off the request thread. The trade is deliberate: billing is eventually consistent, request latency is not negotiable. At 5M requests a month the alternative is a synchronous database write on the hot path of every single call.

The queue’s transaction wrapper retries three times with exponential backoff — that exists specifically because write contention is a real failure mode under load, and losing a billing row is worse than waiting 400ms to write it.

The billing model is the product

The API is the easy half. What makes it a platform is that a developer can sign up, get keys, and be metered without anyone talking to them.

Every request falls through the same four gates, in this order.

Four gates, walked per request. The third is the interesting one — going negative is a deliberate decision, not an oversight.

In words:

  1. Free monthly credits — 3,000 requests by default, refreshed on a configurable reset day. Costs nothing, decrements a counter.
  2. Account balance — once free credits are gone, the request is charged against real money.
  3. Credit limit — the balance is allowed to go negative, to a default floor of −500 EGP, so a client who runs out mid-month doesn’t have their product break while a transfer clears.
  4. Rejection — which should be unreachable, because key validation already pre-checks the first three.

A warning fires below 50 EGP. Suspension only triggers once the balance passes the credit limit, and lifts automatically when an approved payment brings it back to zero. Payments themselves are Vodafone Cash, Instapay and bank transfer, approved by hand.

No payment gateway

Manual approval is the real constraint on this system, not throughput. It works at 50 clients because 50 clients is a small enough number to approve by hand; it is the first thing that breaks at 500. Naming it here rather than describing the platform as if it scales linearly — the code does, the operations don’t.

What’s under the hood

  • 8 Flask Blueprints — v1 and v2 API surfaces, auth, user, payment, admin, plus backup and diagnostic blueprints that only register in debug builds
  • Five backend cache layers — API keys (10,000 entries, 5-minute TTL, LRU that drops the oldest quarter on overflow), endpoint pricing, system settings, per-user stats, payment methods. Every one of them exists because it sat on a hot path.
  • Auth split by audience — sessions for the dashboard, API keys for the API, PBKDF2-SHA256 at 50,000 iterations for passwords, 6-digit OTP with a 15-minute expiry for resets
  • Per-key daily ceiling — 10,000 requests, resetting at midnight UTC, so one misbehaving integration can’t drain an account overnight
  • React Context frontend with per-resource cache lifetimes rather than one global TTL — balance refreshes on a minute, payment methods on ten, because they change at different rates
  • Docker, Gunicorn in production, SQLAlchemy against PostgreSQL
  • ~6,100 lines of documentation across four files, including a 2,200-line integration guide. This was built to be integrated against by people who aren’t me, and that’s the part that made it a product instead of an endpoint.

An MCP server, for agents instead of clients

The REST API above assumes a developer: reads the docs, wires up a client, handles the response shape. An AI agent doing the same task has to be handed all three — the endpoint list, the auth, the parsing — inside its own context window, every single session, before it can act. geolink-mcp is that wiring done once: a Model Context Protocol server that puts geocoding, place search, directions and distance matrix directly into Claude, Cursor, or any MCP-speaking agent, plus two composite tools built for what a raw endpoint can’t answer on its own.

geolink_find_nearest ranks candidates by real road travel time, not straight-line distance — the difference a river or a one-way system makes in Cairo. geolink_sweep_area tiles a whole region with a query grid to find everything of a kind, not one page from one search point, and it always runs a dry_run first so the exact API cost is known before a single credit is spent.

Errors that teach, not just fail

Every tool failure returns in-band as Error (<kind>): <what happened>Next step: <the exact parameter change that fixes it>. A matrix call that exceeds its cell cap doesn’t just error, it names the batch size that would succeed. That single design choice is why an agent using this server self-corrects instead of retrying blind — the same instinct behind Geolink’s own key-validation pre-checks upstream.

Free tier, same key you’d use against the REST API directly — geolink-eg.com/register, no card. The server ships tested (unit + end-to-end against a mock, zero network needed in CI), documented, and open source.

This kind of problem?

I build production systems that answer “where is it now?” — reconciliation engines, geolocation platforms, ride-sharing coordination layers, AI tool servers. If you’re facing a system that needs to survive real traffic, real constraints, and real time — let’s talk.

Usually reply within 24 hours. Platform engagements start at $8K/month.