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.0rc1.tar.gz (632.3 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.0rc1-cp311-abi3-win_amd64.whl (2.6 MB view details)

Uploaded CPython 3.11+Windows x86-64

rookie_cookies-0.6.0rc1-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.0rc1-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.0rc1-cp311-abi3-macosx_11_0_arm64.whl (1.9 MB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

rookie_cookies-0.6.0rc1-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.0rc1.tar.gz.

File metadata

  • Download URL: rookie_cookies-0.6.0rc1.tar.gz
  • Upload date:
  • Size: 632.3 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.0rc1.tar.gz
Algorithm Hash digest
SHA256 c4912a1540fbd7af974b4411f41dada15dc1d5d567a0ba2efbd4ea82336391d6
MD5 641dabf28ac4049329df7b3dd6f6efd9
BLAKE2b-256 cdded627974951003e15c7edb80fcbbed2af8ec510ba7ea5768834aaafdf4654

See more details on using hashes here.

Provenance

The following attestation bundles were made for rookie_cookies-0.6.0rc1.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.0rc1-cp311-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for rookie_cookies-0.6.0rc1-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 8ff096760ac0a1885a5d3a5e99731bfe7ed57730ef74879cafb480458f6f1b35
MD5 017d4a5da6289c3e301ac0fe5bf1de87
BLAKE2b-256 b2d81565fb46bf1d24835820fdd77cbbe601ccc26db9d4ce5e2524e615e9f48c

See more details on using hashes here.

Provenance

The following attestation bundles were made for rookie_cookies-0.6.0rc1-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.0rc1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for rookie_cookies-0.6.0rc1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 42f837add4e8acde75543fcfa54c7ab6ba418e7c1b7b730bbf7aca5089b3dfd2
MD5 acd4ea84c295c7726b4f8f0c311a5dff
BLAKE2b-256 7f9d6de1f504be374502761da81279c301f8bf57d10ea81ea6dd04cebfa2c74a

See more details on using hashes here.

Provenance

The following attestation bundles were made for rookie_cookies-0.6.0rc1-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.0rc1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for rookie_cookies-0.6.0rc1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 2efd1c8c9001dc36ca3c5910e352f5b4b6a245683a2ae31b3e45406903ecce01
MD5 b03cceefe02a2c4c31ca834716ac757a
BLAKE2b-256 b53345973bde03ee447d75f3935e3a8f08751e9fe062f836f02544c73bf09dd4

See more details on using hashes here.

Provenance

The following attestation bundles were made for rookie_cookies-0.6.0rc1-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.0rc1-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for rookie_cookies-0.6.0rc1-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f1ae489d3305bc2b9b7d8072a696cd9fde45ba8ca9bf1bfe93b46677a7f4f8a2
MD5 7df5a9d695dd4d7d7678a4a7028fa64c
BLAKE2b-256 f31878e71f75310a7bf9efbbaed8002305ff6c29c5f42bffd0c0af4454f5e258

See more details on using hashes here.

Provenance

The following attestation bundles were made for rookie_cookies-0.6.0rc1-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.0rc1-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for rookie_cookies-0.6.0rc1-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 eee3198d28e33b3258718ab2ebe7b23aea201ecde492ac1c437850800908bb48
MD5 f59a230a1670ca8f241784df63e3716d
BLAKE2b-256 705d10a386b98c7bb845e48fefb1da2046906a061283ad7563b8b020a33589aa

See more details on using hashes here.

Provenance

The following attestation bundles were made for rookie_cookies-0.6.0rc1-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.0rc1 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