psn_game_scrapper
Export your PlayStation Network (PSN) game library — titles, platform, hours played, last-played date, and platinum status — to a CSV file ready to import into GRAVK.
Built on the unofficial psnawp library
(import name psnawp_api), which talks to the same private PSN endpoints the
PlayStation App uses. There is no official PSN API, so this relies on the
npsso session cookie from your PSN account.
How it works
- Authenticates to PSN using your
npssotoken. - Fetches your title stats (
client.title_stats()) — name, platform, total play duration, last-played date. - Fetches your trophy summaries (
client.trophy_titles()) — used only to determine whether each title has a platinum trophy. - Merges the two by (normalized) title name and writes one CSV row per game.
Installation
pip / pipx
pip install psn-game-scrapper
# or, isolated from your other Python packages:
pipx install psn-game-scrapper
This installs a psn-export command on your PATH. Requires Python 3.9+.
Not published to PyPI yet? Install straight from GitHub:
pipx install git+https://github.com/olegzalipskiy/psn_game_scrapper.git
Homebrew
brew tap olegzalipskiy/psn_game_scrapper https://github.com/olegzalipskiy/psn_game_scrapper
brew install psn-game-scrapper
This also installs the psn-export command. See Formula/psn-game-scrapper.rb.
From source (for development)
git clone https://github.com/olegzalipskiy/psn_game_scrapper.git
cd psn_game_scrapper
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -e ".[dev]"
Getting your npsso token
- Log in to My PlayStation in your browser.
- While still logged in, visit
https://ca.account.sony.com/api/v1/ssocookie. - The page returns JSON like
{"npsso": "64-character-string"}. Copy the 64-character value — that's your token. - The token expires after about 2 months (or immediately if you log out of PSN in that browser), so you may need to repeat this step later.
Keep this token secret — it grants access to your PSN account.
Usage
# Pass the token directly
psn-export --npsso <your-64-char-npsso-token> --output games.csv
# Or set it as an environment variable
export PSN_NPSSO=<your-64-char-npsso-token>
psn-export --output games.csv
# Only export games with at least 1 hour of playtime
psn-export --output games.csv --min-hours 1
# Omit --output to get the default name: psn_library_export_{YYYY-MM-DD}.csv
psn-export
Running from a source checkout without installing the console script also
works: python -m psn_game_scrapper (same flags).
CLI options
| Flag | Required | Default | Description |
|---|---|---|---|
--npsso |
No* | $PSN_NPSSO environment variable |
PSN npsso token |
--output |
No | psn_library_export_{YYYY-MM-DD}.csv |
Output CSV file path |
--min-hours |
No | 0 (export everything) |
Only include games with at least this many hours |
* Either --npsso or PSN_NPSSO must be set; the script exits with an
error message otherwise.
Output CSV format
Matches the GRAVK import template exactly (title,hours,completedAt,platform, status,userScore,platinum,steamDeck,favorite):
- UTF-8 encoded, comma-separated, one header row followed by one row per game.
- Empty/unknown optional fields are written as an empty string, never the
text
Noneornull.
| Column | Source | Notes |
|---|---|---|
title |
title_stats().name |
Required field |
hours |
title_stats().play_duration |
Rounded to the nearest whole hour |
completedAt |
title_stats().last_played_date_time |
Date of the most recent play session (YYYY-MM-DD), empty if PSN has no record of it |
platform |
title_stats().category |
PS4/PS5 are both mapped to PlayStation; anything else passes through unchanged |
status |
— | Always empty — left for GRAVK to auto-detect from hours played, per the GRAVK template's own rule |
userScore |
— | Always empty — PSN has no such field |
platinum |
trophy_titles().earned_trophies.platinum, matched by normalized name |
true/false |
steamDeck |
— | Always false — PSN games are never played on Steam Deck |
favorite |
— | Always empty — left as a GRAVK default |
Note: GRAVK's own review field is not part of the import template, so this
exporter does not produce one either.
Known limitation
Title matching between the "titles you've played" endpoint and the "trophy
summaries" endpoint is done by normalizing and comparing title names (case,
whitespace, ™/® symbols). PSN does not expose one shared ID between these
two endpoints for a user's whole library. In the rare case a game's name
differs meaningfully between the two responses, platinum will read false
for that title even if a platinum was actually earned. Everything else
(title, hours, completedAt, platform) is unaffected, since it
comes entirely from title_stats().
Project layout
src/psn_game_scrapper/
client.py # PSNAWP wrapper: auth, fetching, retry/backoff, data mapping
csv_writer.py # CSV formatting/writing, min-hours filtering
cli.py # CLI entry point (the `psn-export` console script)
Formula/ # Homebrew formula
tests/ # pytest suite (fully mocked, no network/token required)
Running the tests
pip install -e ".[dev]"
pytest tests/ -v
The suite mocks every PSN API call (unittest.mock/pytest-mock) — it
never hits the network and never needs a real npsso token. It covers:
- platform mapping (PS4/PS5 →
PlayStation, unknown platforms pass through) - rounding play duration to whole hours
- platinum detection from trophy data
- CSV header names/order and empty-vs-
Noneformatting - the
--min-hoursfilter - invalid/expired npsso token handling
- exponential-backoff retry behavior on simulated rate-limit (429) errors
To check coverage locally the same way CI does:
pytest tests/ --cov=psn_game_scrapper --cov-report=term-missing --cov-fail-under=80
Continuous integration
Every pull request targeting master runs the test suite via
GitHub Actions and posts a comment on the PR
with the pass/fail results and coverage report. The build fails — blocking
the merge — if any test fails or total coverage drops below 80%. The
pipeline does not run on plain commits/pushes, only on pull requests into
master.
Note: for the check to actually block merging, the repository's branch protection rule for
mastermust have "Require status checks to pass before merging" enabled with theRun tests (min 80% coverage)check selected (Settings → Branches).
Releasing (maintainers)
- Bump
versioninpyproject.tomland__version__insrc/psn_game_scrapper/__init__.py. - Tag the release:
git tag v0.1.0 && git push origin v0.1.0. This triggers.github/workflows/publish.yml, which builds the package and publishes it to PyPI via trusted publishing — no API token needed, but the PyPI project must have a trusted publisher configured for this repo/workflow first (one-time setup on pypi.org). - Update Formula/psn-game-scrapper.rb:
point
urlat the new tag's tarball and refreshsha256withcurl -sL <tarball-url> | shasum -a 256. If any dependency version changed, refresh itsresourceblock the same way (URL + sha256 fromhttps://pypi.org/pypi/<package>/<version>/json).
Troubleshooting
- "The npsso token could not be parsed" — you likely copied a truncated
or malformed value. Re-fetch it from
https://ca.account.sony.com/api/v1/ssocookiewhile logged in. - "PSN rejected the npsso token - it is likely expired or invalid" — npsso tokens expire; generate a new one following the steps above.
- Rate limiting — the script automatically retries PSN rate-limit (429) and server errors with exponential backoff (up to 5 attempts by default); large libraries with hundreds of games may take a few minutes.
Metadata
Release files for psn-game-scrapper 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| psn_game_scrapper-0.1.0.tar.gz | 17.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| psn_game_scrapper-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 30.0 kB
Release files / psn_game_scrapper-0.1.0.tar.gz
| Download URL | psn_game_scrapper-0.1.0.tar.gz |
|---|---|
| Size | 17.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9bf2dcda01a28602a7bf322a807b9db3c6ffad088a67cacd67d975ce4c3a8875
|
|
BLAKE2b-256 checksum How to use checksums |
acb29870186d461b7d85ee81e2d712923928cecfd6291bdc8e8cb4133c004766
|
| 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 Aug 7, 2026.
Transparency logRelease files / psn_game_scrapper-0.1.0-py3-none-any.whl
| Download URL | psn_game_scrapper-0.1.0-py3-none-any.whl |
|---|---|
| Size | 12.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f05c94a6b96c8487775988bccd8332e0542041e6ce2398a71b97b8711aeaeede
|
|
BLAKE2b-256 checksum How to use checksums |
03f23c89a44f6f06f7b495ae53379f8caef37d1e08b5252a41de42deff0b2431
|
| 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 Aug 7, 2026.
Transparency log