SReverseby Simpa Labs

Python clients

Calling an Android app's backend from Python

Writing calls to the endpoints you recovered is the easy part of a Python client. The work that decides whether it still runs next week is reproducing the transport settings, the session, the values the app computes per request, and the order in which calls depend on each other.

What the client has to reproduce

A Python client for a mobile backend is a reimplementation of the app's request pipeline. Endpoints, parameters and response models describe the surface. Underneath that surface sit four things the app does implicitly: it keeps a session alive, it presents itself in a particular way to the server, it computes values that change per request, and it calls endpoints in an order the server enforces.

Build the client in that order, from transport upward, and test each layer against a real request before adding the next. A client that gets the transport right and the signing wrong produces a rejection you can attribute. A client that guesses at both at once produces a rejection you cannot.

Start with transport and session

Use one session object for the whole flow and reuse it everywhere. A session carries the cookie jar, the connection pool and the default headers, and those are exactly the pieces the app relies on between calls. Creating a new client per request breaks cookie continuity and forces a fresh handshake each time.

Match what the app sends beyond the URL: the user agent, the accepted content encodings, the accepted response type, and any header the client library adds by default. Header casing and order matter when the server hashes the header block, so preserve the order you observed in the capture rather than the order a dictionary happens to iterate. Set explicit timeouts, and let the TLS layer verify normally until you have a reason to change it.

Reproduce the signing step

The signing step is the part most likely to break, and there are three workable ways to handle it.

Reimplementing the algorithm in Python is the fastest at runtime and the easiest to maintain, because the client has no device dependency. It requires the exact input construction: which method, path, query, body and headers are concatenated, in what order, and with what separators. A single missing separator changes the digest, so the reimplementation should be tested against real requests until the values match.

Instrumenting the app and calling it as an oracle is the right choice when the algorithm depends on a key you cannot extract or on a native routine you have not fully read. The client sends the request parts to a helper running on a device and receives the signature back. That keeps the client correct while the device is available, and it is a reasonable staging step while the algorithm is being understood.

A third option keeps a small service that performs the signing and nothing else, which separates device dependencies from the client code and makes the client portable.

Model the stateful flows

Mobile APIs are rarely stateless from the client's point of view. A session is established, a device is registered, a token is refreshed, and screens issue calls in a sequence the server validates. Represent that as a class with explicit steps rather than as loose functions, so the order is visible in one place.

Handle re-authentication centrally. When a token expires mid-flow, one component should refresh it and retry the request whose credentials had gone stale, and every call should go through that component.

Watch for server-side expectations that are not in the response body: an idempotency key on a retried write, a cursor that must be carried forward, or a call that has to happen once per session before others are accepted. Those constraints show up as errors when the order is wrong, and they belong in the client's flow code with a comment explaining why the call is there.

Model responses and errors

Give the important responses a typed model so callers work with fields rather than dictionary keys. Keep the raw payload on the object as well, because a mobile API often returns fields that no model documents, and discarding them makes later work harder.

Separate error classes by cause. An authentication failure, a validation failure and a rate limit each need different handling, and collapsing them into one exception type forces callers to inspect status codes by hand. Retry on transport failures and server errors. A refusal driven by a signature or a device check is deterministic, so retrying it only wastes the freshness window.

Validate against the app

Compare the client with the app rather than with your expectations. Log the outgoing request on both sides and diff them field by field, including headers you consider irrelevant. Run the same flow in both and compare the response bodies, which catches parameter mistakes that still return a success code.

Keep the client maintainable

Store recorded responses as fixtures so tests run without a live server, keep base URLs and credentials in configuration, and log the derived values during development so a failure points at the value that changed. A test that asserts the signature of a known request catches a broken algorithm before it reaches a caller.

SReverse delivers a working Python client for the app, including signing and session handling. When a flow spans several endpoints with dependent state, the reconstruction work is described under stateful API workflow reconstruction, and the token side is covered by Android authentication reverse engineering.

Reviewed 28 September 2026 · SReverse research desk

Start your full APK reconstruction

Send the full APK

Projects start at $120. Choose WhatsApp or email, then attach the APK in the app that opens. We reply within one hour with the next step and send the fixed quote after review.

Want us to contact you?