Skip to main content

Narwhal API for Python

Typed synchronous and asynchronous clients for economics, foreign exchange, calendars, physical palm oil, physical commodity reference prices, spot gold and silver, prediction markets, FX and commodity bar history, and webhook delivery. Python 3.10 or newer. Install with pip install narwhalapi.

Set NARWHAL_API_KEY in the environment. Keep keys on the server. The default base URL is https://api.narwhalapi.com; override it with NARWHAL_BASE_URL or base_url=.

Quickstart

>>> from decimal import Decimal
>>> from narwhalapi import Narwhal
>>> with Narwhal() as nw:
...     cpi = nw.economics.cpi("USA")
...     rates = nw.fx.rates("USD", currencies=["EUR", "JPY"])
...     days = list(nw.fx.rate_history_iter(
...         "USD", currencies=["EUR"], start_date="2026-08-20", end_date="2026-08-24"
...     ))
>>> isinstance(cpi.value, Decimal)
True
>>> isinstance(rates.rates[0].rate, Decimal)
True
>>> all(isinstance(rate.rate, Decimal) for day in days for rate in day.rates)
True

Decimals remain exact Decimal values. Use format(value, "f") to display fixed-point wire text, including very small values. Decimal inputs accept Decimal or str; other numeric types raise TypeError. Responses accept only decimal strings matching ^-?\d+(\.\d+)?$ with ASCII digits. Dates and timestamps remain strings.

from narwhalapi import AsyncNarwhal, errors

async def load_rates():
    async with AsyncNarwhal() as nw:
        try:
            return await nw.fx.rates("USD", currencies=["IDR", "SGD"])
        except errors.RateLimited as error:
            if error.is_quota_exhausted:
                return None
            raise

Share a synchronous client across threads. Use one async client per event loop. Context managers close connections; explicit close() and await aclose() also work.

Operations and types

Namespaces are economics, fx, calendars, commodities, events, webhooks, and calendar_subscriptions. Operations not yet in the live API appear under nw.unstable; there are none today. Models and known string constants are available through narwhalapi.models, for example from narwhalapi.models import Cpi. Unknown enum strings are retained. Union variants are frozen dataclasses with a kind; its kind is the variant model name (for example PalmoilSchedulePrice). An unknown variant has kind="unknown" and keeps its JSON in raw. New response fields are ignored.

Required path arguments may be positional. Query and body arguments are keyword-only:

nw.fx.convert_currency(from_="USD", to="IDR", amount="100.00")
nw.fx.rate_history("USD", currencies=["IDR"], start_date="2026-01-01", end_date="2026-01-31")
spot = nw.commodities.spot_price("XAU", "USD")
nw.webhooks.update_destination(
    "destination-id", url="https://example.test/webhook",
    countries=["IDN"], families=["prices"], enabled=False,
)
feed_url = nw.calendar_subscriptions.feed_url("subscription-token")

Updates require enabled explicitly. Creation sends only supplied fields; omitted fields and explicit None remain distinct in request bodies. feed_url() makes no request. Lists use the documented repeated or comma-joined encoding. Scalar strings, empty list items and items containing commas are rejected for list query parameters.

Every paginated operation has an _iter method, yielding items lazily (use async for with an async client). Filters persist across pages; limit is the page size. Cursor expiry raises errors.CursorExpired, including after earlier items were yielded.

Top-level meta contains optional request_id, quota, and short values from response headers. Each rate limit has optional limit, remaining, and reset fields.

Failures and retries

All client failures inherit errors.NarwhalError. errors.ApiError exposes status, code, title, detail, request_id, instance, retry_after, and raw_body bytes. Typed subclasses cover common statuses and cursor expiry. Known codes are exported as uppercase constants in narwhalapi.codes; codes also accept future strings. TransportError.cause preserves the underlying error. DecodeError.raw_body keeps an invalid success response as the original bytes. Import exceptions directly, for example from narwhalapi.errors import RateLimited. API errors display server fields as HTTP <status> <code>: <title> (request_id <id>). All SDK errors support pickling. Client representations exclude credentials.

Defaults are a 30-second attempt timeout, a 10-second connect timeout, two retries, and a 30-second maximum retry wait. Only short-window 429s retry on any method. A 503 retries on GET; a transport failure retries on GET only before any response. Monthly quota exhaustion never retries. Redirects are not followed. Use max_retries=0 to disable retries. Both clients honor standard proxy and certificate environment variables, including HTTPS_PROXY and SSL_CERT_FILE; supply proxy= to select an explicit proxy. extra_headers cannot replace Authorization. The transport, clock, sleep, and random_fraction constructor options support tests; async sleep must be awaitable.

Webhooks

from narwhalapi.webhooks import verify verifies exact raw bytes before decoding. It accepts a nonempty secret string or a nonempty list of nonempty secrets during rotation. Empty secrets raise ValueError before verification. Timestamp tolerance is 300 seconds by default. Use the returned event_id as the idempotency key and store it before applying the event. Future schema versions still decode.

FastAPI:

raw_body = await request.body()
event = verify(raw_body, request.headers, webhook_secret)

Flask:

raw_body = request.get_data(cache=True)
event = verify(raw_body, request.headers, webhook_secret)

Do not parse and re-serialize the body before verification. A verification failure raises errors.WebhookVerificationError with a reason. Neither framework is a package dependency.

Metadata

Release files for narwhalapi 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for narwhalapi 0.2.0
File Size Uploaded Provenance
narwhalapi-0.2.0.tar.gz 42.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for narwhalapi 0.2.0
File Interpreter ABI Platform
narwhalapi-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 88.4 kB

Release files / narwhalapi-0.2.0.tar.gz

Download URL narwhalapi-0.2.0.tar.gz
Size 42.1 kB
Tags Source
SHA-256 checksum
How to use checksums
7fd89aa39f6fd6e3681957602b83c517b7b54e3cb1dc778f2e60f4f66d1a8fe9
BLAKE2b-256 checksum
How to use checksums
2122ee6485982c739a433526e207cadc3f65d3dcc4ec92dc8cee36117b709919
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / narwhalapi-0.2.0-py3-none-any.whl

Download URL narwhalapi-0.2.0-py3-none-any.whl
Size 46.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
101ddf06a2d567e09992e980b1c2f1dbe3448c647bd291535d41016cd50826d0
BLAKE2b-256 checksum
How to use checksums
ca8ce3dbdf7ac683b0928043c24f098dfc7441f7b4e5e114a29d0f673a078330
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for narwhalapi 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page