Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

rookie-cookies (Python)

Extract cookies from local browsers on Linux, macOS, and Windows.

This file is the Python guide (PyPI landing page and repo tutorial). Rust stays in rookie-rs/README.md. The recommended 0.6 entry is jar / read (ADR 0004). Package metadata and version() identify the installed build.

CPython ≥ 3.11. Wheels are cp311-abi3 (tested 3.11–3.14). CPython 3.8–3.10 and PyPy are not supported in 0.6.

pip install rookie-cookies

Windows App-Bound security note: jobs default to unprivileged reflective injection into a spawned browser process, which endpoint security can flag. Pass app_bound="disabled" to read, jar, from_path, or report jobs to perform no App-Bound process work; v20 rows will then be omitted with a warning.

Recommended usage (0.6 series)

import rookie_cookies as cookies

# Gecko session import — profile selection and session policy are independent
session_jar = cookies.jar(
    browser="firefox", profile="default-release", include_session=True
)

# Domain-intact records (storage_state / allowlists)
rows = cookies.read(browser="chrome", profile="Work").as_list()
header = cookies.read(browser="chrome", profile="Default").header(
    "https://example.com/"
)

jar is read(...).as_jar(). read never URL-filters; http.cookiejar owns send-match. There is no module-level header() — call ReadResult.header(...) on a snapshot you already took.

  • No-profile read(browser="chrome") matches chrome() (persistent / legacy-eligible cookies).
  • include_session defaults to False. Naming a Gecko profile alone no longer also acquires its separately declared session JSON source — pass include_session=True to read() / jar() for that. Breaking change from earlier 0.6 prereleases, where naming a profile always included session cookies; this fails quietly (a smaller result, no error) — see CHANGELOG.md.
  • Chromium registrations have no separate session source; include_session is a no-op there regardless.

Named helpers (chrome(), firefox(), load()) still work. They are the compatibility bridge from thewh1teagle/rookie and will break in a later major version. Prefer read / jar for new code.

Isolation-aware cookies

ReadResult.as_list() / .cookies() return the frozen eight-field compatibility projection, which cannot represent a Chromium CHIPS partition key or a Firefox Multi-Account Containers identity. detailed_cookies() returns the native, isolation-intact records instead:

snapshot = cookies.read(browser="firefox", profile="default-release")
for record in snapshot.detailed_cookies():
    cookie, context = record["cookie"], record["context"]
    print(cookie["domain"], cookie["name"], context["user_context_id"])

Each item is {"cookie": <the same 8-field dict>, "context": {...}}, where context carries top_frame_site_key, has_cross_site_ancestor, source_scheme, source_port, is_persistent, origin_attributes, user_context_id, partition_key, and private_browsing_id — every field optional, since browser schemas vary and a missing field means the source never exposed it (not that the cookie has some default value).

Snapshot warnings (ReadResult.warnings, each {"code": ..., "count": ...}) include two isolation-related codes: malformed_host_identity (a row whose host did not survive decode was omitted from the snapshot, not emitted as domain: "") and unparsable_partition_key (retained in the snapshot, but never matched by header() since its partition can't be identified).

Sending cookies (header)

ReadResult.header(...) builds a send-safe Cookie request-header value. A bare URL string covers the common case:

header = snapshot.header("https://example.com/")

Partitioned or containered cookies need a SendContext shaped mapping, or the equivalent keyword arguments, to say which browsing context the request is from — otherwise header() would have to guess, and it refuses to:

header = snapshot.header(
    "https://example.com/",
    top_level_site="https://example.com/",
    resource="navigation",   # or "subresource" (default)
    method="safe",           # or "unsafe" (default "safe")
    user_context_id=0,
    private_browsing_id=0,
)

The positional argument and the keyword arguments compose (an explicit keyword wins over the same-named mapping entry), so header({"url": u, "top_level_site": t}, method="unsafe") is also valid. now overrides the send-time clock with epoch seconds, mainly for tests.

A snapshot that positively observes a partitioned or containered cookie demands the matching selector rather than silently merging it with unpartitioned/default-container cookies: omitting one raises RookieRequestError with code == "incomplete_send_context" and a required attribute naming exactly which selectors (e.g. ["top_level_site"]) were missing.

Selecting profiles (select)

read / jar take select: Literal["legacy_first"] = "legacy_first" — the only value they can express, since a snapshot has exactly one profile_id. browser_report takes select: Literal["legacy_first", "all"] = "all", matching what browser_report(id, None, domains) has always meant. Passing profile=/profile_id= together with select="all" (or passing select="all" to read/jar at all) is a request error with code == "conflicting_profile_selection", raised before any I/O — naming one profile and asking for every profile contradict each other.

Reports

read / chrome() flatten one source. Reports keep every profile and failure visible. Identifiers and codes are open snake_case strings.

from rookie_cookies import browser_profiles, browser_report, supported_browsers

for browser in supported_browsers():
    print(browser["id"], browser["display_name"], browser["engine"])

for profile in browser_profiles("chrome"):
    print(profile["profile"]["profile_id"], profile["profile"]["display_name"])

report = browser_report("chrome", domains=["example.com"])
assert report["schema_version"] == 1
for profile in report["profiles"]:
    for source in profile["sources"]:
        if source["selected"] and source["status"] == "succeeded":
            print(source["source"]["path"], len(source["cookies"]))

Job-layer aliases (same DTO):

import rookie_cookies

descriptors = rookie_cookies.profiles("chrome")
report = rookie_cookies.report(browser="chrome", profile="Default")

A missing install is status == "no_sources", not an empty success. Bad requests raise RookieRequestError; other failures land in the report itself rather than an exception (see Errors). schema_version versions the DTO; reject unknown values. termination (completed, timed_out, cancelled, resource_exhausted) is independent of status. Issues count every hit in occurrences but keep at most MAX_ISSUE_SAMPLES in samples.

supported_browsers() is registration, not detection. chrome() stays default-first. chrome_profiles() / chrome_profile() add activity-hint order and a grouped report; lossy paths need the opaque profile_id. load_report() covers every registered browser.

Explicit paths

extract_from_path is the canonical name for this job (Rust direct_path::extract_from_path, Node extractFromPath, CLI from-path --domains):

from rookie_cookies import extract_from_path

firefox = extract_from_path("/path/to/cookies.sqlite", domains=["example.com"])
chrome = extract_from_path(
    "/path/to/Chrome/Default/Network/Cookies",
    domains=["example.com"],
    browser_id="chrome",
)

At most one of browser_id, local_state_path, plaintext_only=True. Zero selectors identifies the source from its signature and schema, with an encrypted Chromium row rejected (missing_chromium_credentials) rather than guessed at — on every platform, including Windows, which previously required local_state_path even for an all-plaintext database. Request faults on this API are RookieRequestError, not a bare RuntimeError. from_path accepts the same three credential selectors directly (as keyword arguments) for a Chromium path.

Caller-correctable path/source/option failures raise RookieSourceError. Operational inspection failures—such as an I/O, SQLite, locked, or corrupt file failure—raise RookieEngineError/RuntimeError with code == "source_inspection_failed"; diagnostics remain path-sanitized.

cookies_from_path (positional domains, no credential selectors) and chromium_cookies_from_path (an options dict) are deprecated aliases onto extract_from_path — same behavior, kept for earlier 0.6 prerelease callers, not removed outright since deleting them this late buys nothing.

chromium_cookies_from_path_detailed has no replacement of the same shape and no longer accepts a domains option. Isolation-aware output now comes from the core's from_path(..).detailed_cookies(), which has no domain filter of its own — a real narrowing, not a binding limitation. Passing domains to chromium_cookies_from_path_detailed raises RookieRequestError; use extract_from_path for a domain-filtered flat list, or filter from_path(..).detailed_cookies()'s output yourself.

any_browser(), chromium_based*, and flat firefox_based() are deprecated until ≥ 0.7. firefox_based_detailed() stays for container context.

Timeouts and cancellation

read, jar, from_path, extract_from_path, report jobs, and profile-listing jobs accept timeout (seconds) and cancellation. The deprecated named browser helpers keep their old signatures and do not expose those controls.

import threading
import rookie_cookies

cancellation = rookie_cookies.CancellationHandle()
timer = threading.Timer(5, cancellation.cancel)
timer.start()

try:
    rows = rookie_cookies.read(
        browser="chrome",
        profile="Default",
        timeout=30,
        cancellation=cancellation,
    ).as_list()
except rookie_cookies.RookieStoppedError as error:
    if error.stop_reason == "timed_out":
        print("timed out")
    elif error.stop_reason == "cancelled":
        print("cancelled")
    else:
        raise
finally:
    timer.cancel()

Windows App-Bound (v20) recovery

read, jar, from_path, extract_from_path, report, browser_report, and load_report all take an app_bound keyword. It defaults to "injection_only", because Chrome has written App-Bound (v20) cookies on Windows since Chrome 127 — on a current profile essentially every row is v20, so a policy that refused to recover them would return an empty list for the most common Windows case.

import rookie_cookies

# The default already recovers v20 on Windows.
rows = rookie_cookies.read(browser="chrome", profile="Default").as_list()

# Opt out if injection is unwanted; v20 rows are then skipped.
rows = rookie_cookies.read(
    browser="chrome",
    profile="Default",
    app_bound="disabled",
).as_list()

app_bound accepts:

Value What it does
"injection_only" (default) Unprivileged reflective COM injection into a spawned browser process (Chrome 127+).
"disabled" No injection, no spawned process, no process enumeration, no SYSTEM impersonation. v20 rows are skipped and counted as decrypt_failed warnings.
"allow_elevated_fallback" Injection, then permits elevated SYSTEM impersonation as a fallback (Chrome 133+). Never a default.

"injection_only" is not free of consequence. It spawns a browser process and reflectively injects into it, which endpoint security products can flag. On a managed machine where that matters, pass "disabled" explicitly and expect v20 rows to be omitted.

It is a no-op off Windows — macOS and Linux Chrome use the Keychain and Secret Service, which this policy has nothing to do with. An unrecognized string is a RookieRequestError raised before any I/O. browser_profiles / chrome_profiles do no App-Bound work and take no app_bound parameter at all.

The deprecated v0.5.9 bridge functions (chrome(), chromium_based(), and friends) keep allow_elevated_fallback, unchanged from 0.5.8. See CHANGELOG.md.

Errors

Every exception this module raises is a RookieError, and every one also keeps a second, pre-existing base so old except ValueError / except RuntimeError code keeps working:

Class Also subclasses kind Raised when
RookieRequestError ValueError "request" Caller input was invalid (unknown browser/profile, bad option)
RookieSourceError (subclasses RookieRequestError) ValueError "source" A caller-correctable explicit path/source/option was invalid
RookieStoppedError RuntimeError "stopped" A timeout elapsed, cancellation fired, or an internal resource limit was hit
RookieEngineError RuntimeError "engine" Discovery, acquisition, decryption, or source inspection failed

RookieSourceError subclasses RookieRequestError rather than sitting beside it under RookieError, so except RookieRequestError (or except ValueError) written before this class existed keeps catching an invalid explicit path. Breaking change from the earlier two-class split: a timeout or cancellation used to raise RookieEngineError (there was no "stopped" kind yet); it now raises RookieStoppedError, so code that caught RookieEngineError to inspect stop_reason must catch RookieStoppedError instead — see CHANGELOG.md.

Every class exposes stable kind, code, and stop_reason attributes. Current stop_reason values are timed_out, cancelled, and resource_exhausted, only ever set on RookieStoppedError; treat the attribute as an open string for forward compatibility. Ambiguous profile errors also carry opaque profile_ids; direct-path errors carry source_kind, target_os, and a path_redacted flag; an incomplete header() context (code == "incomplete_send_context") carries a required list naming the missing selectors, e.g. ["top_level_site"] — empty on every other error. Human-readable exception text remains diagnostic only.

Netscape

from rookie_cookies import chrome, to_netscape

output = to_netscape(chrome())

Tabs / CR / LF in cookie fields become %09 / %0D / %0A. Same bytes as Rust, CLI, and Node for the same cookies.

0.5.6 API

In the 0.5.6 line the public surface was the flat named-browser helpers. There was no read / jar job API, no typed RookieRequestError / RookieEngineError split, and no canonical path builders.

import rookie_cookies

cookies = rookie_cookies.chrome()
cookies = rookie_cookies.firefox(["example.com"])
all_cookies = rookie_cookies.load()
jar = rookie_cookies.to_cookiejar(cookies)
path_cookies = rookie_cookies.firefox_based("/path/to/cookies.sqlite")

Wheels were cp38-abi3 until the 0.6 break.

Migrate 0.5.6 → 0.6.0

Area 0.5.6 / early 0.5.x 0.6.0
Recommended entry chrome() / to_cookiejar(...) jar(browser=..., profile=...) or read(...).as_list()
Gecko session cookies Not a first-class policy Pass include_session=True to read / jar; profile= is optional and only selects which profile
CPython 3.8-era / cp38-abi3 ≥ 3.11, cp311-abi3
Path APIs firefox_based, chromium_based, any_browser extract_from_path (cookies_from_path / chromium_cookies_from_path are deprecated aliases onto it, kept until ≥ 0.7)
Path request faults Flat RuntimeError RookieRequestError (ValueError subclass)
Header view Manual / to_cookiejar ReadResult.header(url) or header(context, ...)no module-level header()
Isolation (CHIPS / containers) Not in 0.5.6 ReadResult.detailed_cookies()
Reports Not in 0.5.6 report(...) / browser_report(...), profiles(...)
Windows App-Bound (v20) Always allow_elevated_fallback (named helpers only) app_bound="injection_only" by default on jobs; pass "disabled" to opt out or "allow_elevated_fallback" to permit SYSTEM fallback
  1. Bump to CPython 3.11+.
  2. For Gecko session import, pass include_session=True; add profile= only when you need a profile other than the legacy-first choice.
  3. Keep named helpers only for the frozen compatibility set.
  4. Move explicit DB paths off *_based / any_browser and onto extract_from_path.
  5. Catch RookieError for every library failure, or catch the specific request/source/stopped/engine subclass you intend to handle.
  6. Do not invent a top-level header().
  7. Use detailed_cookies() where a CHIPS partition or Firefox container matters.

See CHANGELOG.md.

Logging

import logging
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)

Disable with logging.CRITICAL.

More

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rookie_cookies-0.6.0b3.tar.gz (628.8 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

rookie_cookies-0.6.0b3-cp311-abi3-win_amd64.whl (2.6 MB view details)

Uploaded CPython 3.11+Windows x86-64

rookie_cookies-0.6.0b3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.9 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ x86-64

rookie_cookies-0.6.0b3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.8 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

rookie_cookies-0.6.0b3-cp311-abi3-macosx_11_0_arm64.whl (1.9 MB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

rookie_cookies-0.6.0b3-cp311-abi3-macosx_10_12_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.11+macOS 10.12+ x86-64

File details

Details for the file rookie_cookies-0.6.0b3.tar.gz.

File metadata

  • Download URL: rookie_cookies-0.6.0b3.tar.gz
  • Upload date:
  • Size: 628.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for rookie_cookies-0.6.0b3.tar.gz
Algorithm Hash digest
SHA256 49c26428af4f19982d098d3828fbafb6093f5d853655642f96e8013bb734a561
MD5 0957e70e5717a737f970959f0f99a94d
BLAKE2b-256 4499bdd85ce623c56d297473bd29f68ed0e29b6980250e8ee409e7d856cf5967

See more details on using hashes here.

Provenance

The following attestation bundles were made for rookie_cookies-0.6.0b3.tar.gz:

Publisher: publish-py.yml on teng-lin/rookie-cookies

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rookie_cookies-0.6.0b3-cp311-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for rookie_cookies-0.6.0b3-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 05828d0b1b77368ca0854e3b752ce55469c4df5cba1c397504e160120da8d209
MD5 d0db3a12339d25f4257495b934453601
BLAKE2b-256 9b92f41aab95c0e288288cb892d8a1eea86a22a9c83b43b48e16c02080c63498

See more details on using hashes here.

Provenance

The following attestation bundles were made for rookie_cookies-0.6.0b3-cp311-abi3-win_amd64.whl:

Publisher: publish-py.yml on teng-lin/rookie-cookies

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rookie_cookies-0.6.0b3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for rookie_cookies-0.6.0b3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 1bfd36bd7c17885bc73dc36cf4ee05005dffbb8fa343499c890066875246f10a
MD5 1461eeac5f0de84c1399d6224b3d1ffb
BLAKE2b-256 74fcf057844bbbc6e39ef99be422d50d77f5c23df400bb09bb1e68b897cf0dac

See more details on using hashes here.

Provenance

The following attestation bundles were made for rookie_cookies-0.6.0b3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish-py.yml on teng-lin/rookie-cookies

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rookie_cookies-0.6.0b3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for rookie_cookies-0.6.0b3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 1959df63b51af7ad2c87b98c80c6a7a965a5390430ca9ff615fe73ffede6caff
MD5 aed53fcf560fcfe1aecf93fc7addcd46
BLAKE2b-256 2453bde4f7a79004a5c007a487ee34e1acfc3538c2eb76cbe0892c5af02b7fe0

See more details on using hashes here.

Provenance

The following attestation bundles were made for rookie_cookies-0.6.0b3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish-py.yml on teng-lin/rookie-cookies

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rookie_cookies-0.6.0b3-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for rookie_cookies-0.6.0b3-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c39ef2f37af1b4346cc44f7a7433d14f1d6941c9ae71203ade331293b2c9395a
MD5 95df996b71610c729bf52c72b672f368
BLAKE2b-256 34e9cbb3d8174d046e81d0aa11a54491742dd62bd917cdd0749d47ff7cc98bf5

See more details on using hashes here.

Provenance

The following attestation bundles were made for rookie_cookies-0.6.0b3-cp311-abi3-macosx_11_0_arm64.whl:

Publisher: publish-py.yml on teng-lin/rookie-cookies

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rookie_cookies-0.6.0b3-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for rookie_cookies-0.6.0b3-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 9a24a69dbd2183b0d0c605cd1c07689130a580bf52b66ce0e811537338014f2d
MD5 b08924a170f7a866ab561927d900af0c
BLAKE2b-256 71eda2b517dfd259f8f61c2359f53b46e81b3e8f274dffaa82cd380f38d244ef

See more details on using hashes here.

Provenance

The following attestation bundles were made for rookie_cookies-0.6.0b3-cp311-abi3-macosx_10_12_x86_64.whl:

Publisher: publish-py.yml on teng-lin/rookie-cookies

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.6.0

6 files

This release

0.6.0b3 This release

6 files

0.5.9

10 files

0.5.8

10 files

0.5.7

10 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