Skip to main content

JavaScript-Fetch-API

A Python 3.14 implementation of the core JavaScript Fetch API, built with the standard library. There are no runtime dependencies. The HTTP transport uses urllib.request; requests run on background threads so await does not block the asyncio event loop.

Install

python -m pip install JavaScript-Fetch-API

The Python import remains from javascript import fetch.

GET and JSON

Replace the example URL with an HTTP(S) endpoint that returns JSON:

import asyncio

from javascript import JSONObject, fetch


async def main() -> None:
    response = await fetch("https://example.com/api/profile")  # method defaults to GET
    if not response.ok:
        raise RuntimeError(f"HTTP {response.status}: {response.statusText}")

    data = await response.json()
    if isinstance(data, JSONObject):
        print(data["name"])
        print(data.name)  # attribute access works too


asyncio.run(main())

Like JavaScript, fetch() returns a Promise[Response]. response.json() returns another promise, which resolves to parsed data. JSON objects become dictionary-compatible JSONObject instances, including objects nested inside objects or arrays. JSON arrays become lists, and strings, numbers, booleans and null become Python strings, numbers, booleans and None.

Attribute access is a Python convenience. Use brackets for keys with spaces or keys matching dictionary methods, such as data["items"] or data["get"]. You can serialize a JSONObject with the standard json.dumps() function.

Parameters

fetch(resource, options=None, **keywords) -> Promise[Response]

resource is an absolute http:// or https:// URL, or a Request instance. There is no document base URL for relative URLs. URL credentials are rejected. options is an optional mapping. You may also use Python keyword arguments; keywords override matching settings in the mapping.

Option Accepted value Default
method HTTP method string, such as GET, POST, PUT, PATCH, DELETE, HEAD or OPTIONS GET
headers Mapping of string names to string values, iterable of (name, value) pairs, or Headers Empty
body str, bytes, bytearray, memoryview, or None None
redirect "follow", "error", or "manual" "follow"
timeout Positive, finite seconds, or None; limits individual socket operations None

timeout is a Python extension, not a JavaScript Fetch option. When resource is a Request, omitted options inherit that request's settings. GET and HEAD cannot include a body, even an empty one. CONNECT, TRACE and TRACK are rejected. A string body receives Content-Type: text/plain;charset=UTF-8 unless you supply a content type; byte bodies receive no automatic content type.

For a JSON request, serialize explicitly, as you would with JavaScript's JSON.stringify():

import json

response = await fetch(
    "https://example.com/api/profile",
    {
        "method": "POST",
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({"name": "Ada"}),
    },
)

The equivalent keyword form is await fetch(url, method="POST", headers=headers, body=json.dumps(data)).

Response

The fetch promise resolves when the server's response headers are available. The body is read and buffered separately; its promise may still be pending or may reject if the connection fails after the headers arrive.

Property Meaning
ok True when 200 <= status <= 299
status HTTP status code
statusText HTTP reason phrase, such as "OK"
headers Case-insensitive Headers collection; fetched response headers are immutable
url Final URL after redirects, without its fragment
redirected Whether at least one redirect was followed
type "basic" for fetched HTTP responses; "default" for constructed responses
bodyUsed Whether this request or response body has been consumed
Method Promise result
json() JSONObject, list, or JSON primitive, depending on the document
text() UTF-8 string; invalid sequences are replaced and an initial BOM is removed
bytes() Python bytes
arrayBuffer() Python bytes, representing the JavaScript binary result

Body readers consume a body once. Calling another reader rejects with TypeError, including after JSON parsing fails. Clone before reading if you need more than one representation:

response = await fetch(url)
copy = response.clone()
data = await response.json()
text = await copy.text()

HEAD responses and statuses 204, 205, and 304 have a null body. Their text/binary readers return empty values without setting bodyUsed; json() rejects because an empty body is not JSON.

HTTP 404 and 500 fulfill with a response whose ok is False. Invalid requests reject with TypeError; network failures reject with FetchError, a TypeError subclass. Invalid JSON rejects its reader with json.JSONDecodeError. Check the HTTP status yourself when your application treats it as an error.

You can construct a response locally with Response('{"name":"Ada"}', status=200, headers={"Content-Type": "application/json"}).

Promises

Promises support await, .then(on_fulfilled, on_rejected=None), .catch() and .finally_(). Python reserves finally, so the method has a trailing underscore. Handlers return a value or another Promise; returned promises are adopted, and exceptions raised by handlers reject the next promise.

from javascript import fetch


def read_json(response):
    if not response.ok:
        raise RuntimeError(f"HTTP {response.status}")
    return response.json()


completed = (
    fetch("https://example.com/api/profile")
    .then(read_json)
    .then(lambda data: print(data.name))
    .catch(lambda error: print(f"Request failed: {error}"))
    .finally_(lambda: print("Finished"))
)
completed.result()  # blocking convenience for a synchronous script

Promise(executor) immediately calls executor(resolve, reject) and accepts only the first resolution or rejection. Rejection reasons use Python exceptions. Promise.resolve(value), Promise.reject(error), Promise.all(iterable) and Promise.race(iterable) are also available. all() preserves input order and rejects on the first failure. race() adopts the first settled outcome.

Use await inside asynchronous code. .result(timeout=...) blocks its calling thread and only limits how long that caller waits; it does not stop the request. Cancelling an asyncio waiter also leaves the shared promise running. Handlers execute serially on a background callback thread; Python does not reproduce JavaScript's event-loop microtask scheduling. Handlers should return promises instead of blocking on .result() or returning raw Python coroutines.

Headers and Request

Headers provides .get(), .set(), .append(), .delete(), .has(), .entries(), .keys() and .values(), plus normal mapping access. Names are case-insensitive, duplicate values are combined with ", ", and invalid HTTP names or values are rejected. Missing .get() lookups return None.

Request(url, options=None, **keywords) supports the same options as fetch() and exposes url, method, headers, redirect, timeout, bodyUsed, and the same body readers. A request with a body is consumed when fetched; call .clone() beforehand to reuse it. Requests without bodies may be reused.

Compatibility scope

This implements core HTTP(S) fetch behavior in Python, rather than a complete browser environment. Bodies are buffered in memory; ReadableStream, Blob, FormData, and URLSearchParams objects are not provided. gzip and deflate response encodings are decoded with the standard library. Other response content encodings reject the body reader with FetchError.

Browser policies and options such as mode, credentials, cache, referrer, referrerPolicy, integrity, keepalive, signal, priority, and duplex are not implemented; unsupported options are rejected explicitly. There is no cookie jar, browser cache, origin policy, or abort controller. Proxy environment settings follow urllib.request defaults.

Redirects follow Fetch's method/body rules, with a limit of 20. Cross-origin redirects remove authorization and cookie headers. redirect="error" rejects; redirect="manual" exposes the actual redirect status, headers and body, instead of a browser's filtered opaqueredirect response.

References: Fetch Standard, ECMAScript Promise specification, and Python urllib.request.

Verification

uv run python -m unittest discover -s tests -v

The tests use only the standard library and local loopback HTTP servers. To run without installing the package or its build backend:

PYTHONPATH=src python3 -m unittest discover -s tests -v

Metadata

Release files for JavaScript-Fetch-API 0.1.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 JavaScript-Fetch-API 0.1.0
File Size Uploaded
javascript_fetch_api-0.1.0.tar.gz 11.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for JavaScript-Fetch-API 0.1.0
File Interpreter ABI Platform
javascript_fetch_api-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 25.6 kB

Release files / javascript_fetch_api-0.1.0.tar.gz

Download URL javascript_fetch_api-0.1.0.tar.gz
Size 11.9 kB
Tags Source
SHA-256 checksum
How to use checksums
74b84591e3e5dddc6f5c69f5eed2496dd540f823656dc686020290e2a3238fca
BLAKE2b-256 checksum
How to use checksums
dc1d3a9972bc4fc0ff98d290211fb78e39a1e536e8210e218b6e4e41f83431a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / javascript_fetch_api-0.1.0-py3-none-any.whl

Download URL javascript_fetch_api-0.1.0-py3-none-any.whl
Size 13.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5cd6ac58b5faaef522552d6e312e07aa7e9672cbcfd3481d5ddd33c17e61f869
BLAKE2b-256 checksum
How to use checksums
b44803698788a2f3d255cdd7f9173e3457896f34c625a144167a355474750195
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.0 This release

2 release files

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