Home / Blog / Guides

Guides

FOMO API Python Quickstart: Profiles and Positions

A production-minded Python quickstart for resolving a FOMO trader and fetching the positions currently available for that account.

Pixel-art Python data cable connecting a trader profile, API terminal, and two blockchain wallet records.

FOMO API Python integrations usually begin with two requests: resolve a FOMO Family handle, then fetch the positions available for that trader. This quickstart builds a small Python client for those calls, including URL encoding, timeouts, credit headers, and errors you can surface instead of hiding.

The examples use the documented origin, https://api.fomoapi.io. Create a key in the dashboard, keep it on your server, and use the API reference as the contract for current response fields.

Install the HTTP client

Python's requests package gives the example connection pooling and explicit timeout handling:

python -m pip install requests
export FOMO_API_KEY="your-key"

Do not paste a real key into source code or commit it to a notebook. In production, load it from the secret store used by your application platform.

Build one reusable client

Use a Session so repeated calls reuse connections. The client below also reads the credit headers and retains the response body when the API returns a useful error object.

import os
from dataclasses import dataclass
from typing import Any
from urllib.parse import quote

import requests

API_ORIGIN = "https://api.fomoapi.io"


class FomoApiError(RuntimeError):
    def __init__(self, status: int, body: Any):
        super().__init__(f"FOMO API returned HTTP {status}")
        self.status = status
        self.body = body


@dataclass(frozen=True)
class FomoResult:
    data: dict[str, Any]
    credit_cost: int | None
    credits_remaining: int | None


class FomoClient:
    def __init__(self, api_key: str, timeout_seconds: float = 15.0):
        self.timeout_seconds = timeout_seconds
        self.http = requests.Session()
        self.http.headers.update({"Authorization": f"Bearer {api_key}"})

    @staticmethod
    def _integer_header(response: requests.Response, name: str) -> int | None:
        value = response.headers.get(name)
        return int(value) if value is not None else None

    def get(self, path: str, params: dict[str, Any] | None = None) -> FomoResult:
        response = self.http.get(
            API_ORIGIN + path,
            params=params,
            timeout=self.timeout_seconds,
        )
        try:
            body = response.json()
        except requests.exceptions.JSONDecodeError:
            body = {"message": response.text[:500]}

        if not response.ok:
            raise FomoApiError(response.status_code, body)

        return FomoResult(
            data=body,
            credit_cost=self._integer_header(response, "x-credits-cost"),
            credits_remaining=self._integer_header(
                response, "x-credits-remaining"
            ),
        )

    def trader(self, handle: str) -> FomoResult:
        normalized = handle.removeprefix("@").strip()
        return self.get("/v2/users/" + quote(normalized, safe=""))

    def positions(self, handle: str, limit: int = 25) -> FomoResult:
        normalized = handle.removeprefix("@").strip()
        path = "/v2/users/" + quote(normalized, safe="") + "/positions"
        return self.get(path, params={"limit": limit})

The timeout applies to both connection and response work. A production service can pass a (connect, read) timeout tuple if those phases need separate limits.

Resolve the trader first

The profile response is the identity step. It can contain the normalized handle, display name, verified state, both-chain wallets, PnL windows, volume, trade count, followers, and account-age fields.

api_key = os.environ.get("FOMO_API_KEY")
if not api_key:
    raise RuntimeError("FOMO_API_KEY is required")

client = FomoClient(api_key)
profile = client.trader("@frankdegods")

print(profile.data["handle"])
print(profile.data.get("wallets", {}))
print({
    "cost": profile.credit_cost,
    "remaining": profile.credits_remaining,
})

A wallet value can be null while resolution is incomplete. Preserve that state. Substituting a guessed address can attach later chain data to the wrong trader.

Fetch the available positions

The current route is GET /v2/users/{handle}/positions. Use it for new code instead of either older trade-route name.

history = client.positions("frankdegods", limit=25)

for position in history.data.get("trades", []):
    token = position.get("token") or {}
    print({
        "symbol": token.get("symbol"),
        "address": token.get("address"),
        "status": position.get("status"),
        "realized_pnl_usd": position.get("realizedPnlUsd"),
        "unrealized_pnl_usd": position.get("unrealizedPnlUsd"),
    })

print({
    "partial": history.data.get("partial", False),
    "complete": history.data.get("complete", False),
    "next_cursor": history.data.get("nextCursor"),
})

Do not describe this result as a complete lifetime ledger. FOMO exposes open positions and bounded closed-position history. A response can identify partial coverage, and deeper reads can consume additional upstream calls. Only request deeper history when the product needs it.

Handle errors by status

Catch transport failures separately from API responses. A timeout means you do not know whether a response was produced; a 401 means the key should be fixed, not retried; a 402 means the credit balance needs attention; and a 429 should honor Retry-After before a bounded retry.

try:
    profile = client.trader("frankdegods")
except requests.Timeout:
    print("FOMO API timed out; retry with a bounded backoff")
except requests.ConnectionError:
    print("network connection failed")
except FomoApiError as error:
    if error.status in (401, 402, 404):
        print("request needs intervention", error.body)
    elif error.status == 429 or error.status >= 500:
        print("request may be retryable", error.body)
    else:
        raise

The full errors, retries, and credits guide includes a bounded backoff policy. Do not retry every failure, and do not let multiple workers create a synchronized retry storm.

Ship it safely

Before this code reaches production:

  1. Keep FOMO_API_KEY in server-side secrets.
  2. Encode user-supplied handles before building the path.
  3. Set connection and read timeouts.
  4. Validate fields before storing them in a typed database.
  5. Preserve coverage flags and null wallets in your product state.
  6. Log status, latency, endpoint, and credit headers without logging authorization.
  7. Cache or coalesce repeated profile reads when fresh data is not required.

If your application is written in Node instead, the TypeScript quickstart implements the same two-call flow with typed responses.

Sources

  1. FOMO API OpenAPI 3.1 specification FOMO API, accessed 2026-09-22
  2. FOMO API documentation FOMO API, accessed 2026-09-22

Ship on fomo.family trader data

Both-chain wallets, PnL and holdings, plus two live streams: the app feed on every plan, the on-chain stream on Growth. One API.

Get an API key

FAQ

Which Python package does this FOMO API example use?
The example uses requests because it provides connection reuse, explicit timeouts, and familiar exception types. Install it with pip install requests and keep your FOMO API key in a server-side environment variable.
Does the positions response contain a complete lifetime trade history?
No. Treat the partial and complete fields as product state. The API exposes the position history currently available from FOMO; applications needing a complete ledger should resolve the wallets and use appropriate chain data.
Can I put the FOMO API key in a Python notebook?
Use an environment variable or a notebook secret store. Do not commit the key in the notebook or print request headers in shared output.