Browser-grade API gateway for a platform with no API
A production REST API and a Kubernetes fleet of browser workers in front of a large consumer web platform that offers no public API.
- Domain
- Integration and automation
- Our role
- Reverse engineering, system architecture, backend development, DevOps and ongoing support
- Year
- 2025-2026
We built a stable, documented REST API for a business whose operations depend on a large consumer web platform with no public API. The platform signs requests with VM-obfuscated in-page JavaScript encryption and sits behind a WAF and anti-bot layer, so the gateway runs every call inside a real Playwright browser session. A Kubernetes fleet of browser workers, orchestrated by Temporal, handles account onboarding, code activation and top-up orders asynchronously, with monitoring built in.
Challenge
- The client's business depended on a third-party platform with no partner API: profile lookups, code checks, activations and top-up orders were available only through its website.
- The site builds and signs request payloads with VM-obfuscated in-page JavaScript encryption, executed by a bytecode interpreter embedded in the page, so ordinary HTTP clients cannot produce valid calls.
- A WAF and anti-bot layer challenges traffic that does not behave like a consistent browser session, and the platform's risk controls change over time.
- Operations need authenticated sessions that are created, verified, refreshed and retired, each tied to a region-matched network route.
- Partners expect a stable contract, idempotent order submission, predictable latency on reads and clear final statuses, while the upstream changes without notice.
Solution
We treat a real browser as the execution engine and put a clean API in front of it. After reverse-engineering the platform's in-page encryption far enough to understand how payloads are formed, we chose not to re-implement it: the gateway calls the platform's own signing code inside warm browser sessions, so it works through the same flows a real user's browser does and stays correct when the upstream protection changes. A FastAPI gateway serves fast reads from a pool of browser contexts on one shared Chromium per pod. Long operations run as Temporal workflows on a dedicated worker fleet with retries and time budgets. PostgreSQL holds accounts, sessions and a multi-provider route pool, and a monitoring service alerts the team chat.
Architecture
What the system does
- Three API surfaces
- Internal, partner and admin APIs, each with its own OpenAPI docs and role-based API keys. The partner API has a trimmed, stable response contract.
- Asynchronous orders
- Orders are accepted with 202 Accepted and an optional idempotency key, then polled for a result. Internal callers also get a synchronous variant.
- Fast reads on warm contexts
- Profile lookups and code checks run as a single in-page call on a ready browser context, so they return in about the time of one upstream request.
- Durable account and session lifecycle
- Account onboarding, session warm-up and scheduled session refresh run as Temporal workflows, together with route pool sync and usage-log cleanup.
- Two activation strategies
- A high-throughput conveyor with pooled sessions, and a sequential mode that keeps work tied to an established account.
- Monitoring and chat-ops
- Alerts on queue growth, failure rates and route health go to a team chat. Operators switch route providers on or off from the chat without a redeploy.
Engineering decisions
- Browser pool rebuilt after a production leak: one process per instance was leaking process trees, so we moved to one shared Chromium per pod with a context pool, least-recently-used balancing, a 15-second health loop, graceful rotation and a fail-fast exit that lets Kubernetes restart the pod clean.
- Consistent browser identity: each session runs under one identity, is warmed up the way a real visitor uses the site and reuses its cookies, which is the main reason the integration stays stable under the WAF and anti-bot layer.
- No race conditions on shared pools: account and route are reserved in one transaction with row-level locking that skips locked rows, and database transactions never span external I/O.
- Classified upstream responses decide whether a worker keeps its session or restarts on a fresh browser, account and route; retry backoff fits inside each order's time budget.
- Each concern is its own Kubernetes deployment scaled by replica count, with a memory-backed shared-memory volume for Chromium, probes, and one schema owner with 32 versioned Alembic migrations.
Interface
Diagrams
Stack
- Python 3.12
- FastAPI
- Playwright
- Chromium
- Temporal
- PostgreSQL
- SQLAlchemy
- Alembic
- Docker
- Kubernetes
- Traefik
- S3
- structlog
- Locust
What this shows
- A reliable, documented API on top of a platform that offers none, including reverse engineering of VM-obfuscated in-page JavaScript encryption.
- Browser-based backends that hold up in production: pooling, lifecycle, crash recovery and process hygiene in containers.
- Long-running, failure-prone business processes modelled as durable workflows with clear final states.
- Long-term support of a production system while the upstream platform keeps changing, with incident analyses feeding back into the design.
Questions about this case
Why run real browsers instead of calling the platform's HTTP endpoints directly?
The platform signs every request with VM-obfuscated in-page JavaScript and checks that traffic behaves like a consistent browser session. Running the platform's own signing code in a real Playwright session produces correctly formed requests without copying proprietary logic, and keeps working when the protection is rebuilt.
What happens to an order if the upstream session breaks halfway?
The worker classifies the response. If the session is no longer usable, it retires the session, releases the route and Temporal retries the order on a fresh worker within the order's time budget. A definitive rejection fails the order at once with a clear reason, and a failure screenshot is linked from it.
How does the system scale?
Each worker type is a separate Kubernetes deployment that runs one activity at a time on a warm browser context. Throughput grows by adding pods, and the gateway's readiness probes drain traffic away from pods without a ready context. Load and latency were tested with Locust.