{"id":37063326,"url":"https://github.com/foobuzz/statify","last_synced_at":"2026-01-14T07:08:44.582Z","repository":{"id":53590057,"uuid":"300942855","full_name":"foobuzz/statify","owner":"foobuzz","description":"Pull your playlist and listening data from the Spotify API","archived":false,"fork":false,"pushed_at":"2022-11-24T19:57:23.000Z","size":442,"stargazers_count":20,"open_issues_count":3,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-11-28T00:25:34.458Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/foobuzz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-03T17:54:28.000Z","updated_at":"2025-02-17T19:00:42.000Z","dependencies_parsed_at":"2023-01-22T03:51:08.949Z","dependency_job_id":null,"html_url":"https://github.com/foobuzz/statify","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/foobuzz/statify","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foobuzz%2Fstatify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foobuzz%2Fstatify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foobuzz%2Fstatify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foobuzz%2Fstatify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/foobuzz","download_url":"https://codeload.github.com/foobuzz/statify/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foobuzz%2Fstatify/sbom","scorecard":{"id":406394,"data":{"date":"2025-08-11","repo":{"name":"github.com/foobuzz/statify","commit":"4e4e19cbce120ff74bf755626e3a6b165add1174"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/python.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python.yaml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/foobuzz/statify/python.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python.yaml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/foobuzz/statify/python.yaml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/python.yaml:17","Warn: pipCommand not pinned by hash: .github/workflows/python.yaml:18","Warn: pipCommand not pinned by hash: .github/workflows/python.yaml:19","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: license.txt:0","Info: FSF or OSI recognized license: MIT License: license.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-18T21:23:24.048Z","repository_id":53590057,"created_at":"2025-08-18T21:23:24.049Z","updated_at":"2025-08-18T21:23:24.049Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28412539,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-14T07:08:43.833Z","updated_at":"2026-01-14T07:08:44.569Z","avatar_url":"https://github.com/foobuzz.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Statify\n=======\n\nFetch your Spotify data (playlists and listenings) and add it to a Sqlite database. See [examples of queries](https://github.com/foobuzz/statify/blob/master/queries.sql) you can then run on this database.\n\n\n## Installation\n\nImportant: Statify requires Sqlite 3.24.0+: \n\n```\npython -c 'import sqlite3; print(sqlite3.sqlite_version)'\n# =\u003e 3.31.0\n```\n\n\n 1. Install the Python package:\n\n```\npip install statify\n```\n\n 2. Create your own [Spotify App](https://developer.spotify.com/dashboard/applications). When you create the App in Spotify, you need to specify `http://localhost:9090` as the redirect URL. You can then add the App's credentials in the statify config file (`~/.config/statify.yaml`).:\n\n```\nspotify_app:\n  client_id: your_app_id\n  client_secret: your_app_secret\n```\n\n 3. Authorize yourself on your App:\n\n```\nstatify auth [--headless]\n```\n\nBy default the `auth` command will open the default browser and ask you to consent to Statify fetching your playlists and listenings. If you're installing Statify on a headless machine (e.g. Raspberry Pi on SSH), then you need to use the `--headless` option so that instead of opening the default browser, a URL is printed to stdout, and you're responsible to visit it and then paste the URL you're redirected to.\n\n\n## Usage\n\nUpdate the database with the state of all your playlists:\n\n\tstatify pull playlists\n\nUpdate the database with your latest listenings:\n\n\tstatify pull listenings\n\tMatch previous listenings fetch at 2020-07-04T16:17:54.247Z\n\tAdded 21 listenings. Newest played_at is now 2020-07-05T08:52:10.641Z\n\nThe Spotify API only gives access to the latest 100 listenings, so you should run the last command as a cron regular enough not to have holes in your listenings history.\n\nYou can also update everything (playlists and listenings):\n\n\tstatify pull\n\nThe database is located at `~/.data/statify/statify.sqlite`. See [examples of queries](https://github.com/foobuzz/statify/blob/master/queries.sql) you can then run on this database.\n\n\n### Webserver\n\nStatify also comes with the command `statify_webserver` to run a webserver on localhost:5000, which features a web interface to search for songs and look at the listenings for this song.\n\n![Statify web interface](docs/statify_webserver.png)\n\n\n## FAQ\n\n**Why do I need to create an App on Spotify and authorize myself on it?**\n\nSpotify has no \"private app\" or \"script app\" mechanism that would allow individual users to use the Spotify API on their own account. So the only way to consume the Spotify API as a given user is to create an App with OAuth 2.0 authorization and authorize oneself on it.\n\nStatify could have been a website running a single App and fetching data about multiple users having authorized it, but as a developer I don't want to maintain such service, and as a user you shouldn't have your data be handed to a third-party in order to get it.\n\n\n**What about data exports?**\n\nSpotify allows users to export their data [from there](https://www.spotify.com/ca-en/account/privacy/). Such data exports don't contain the entire history but only one year of history, which is still better than Statify which can only grab data from the moment it is installed as a cron. However, Statify has the following advantages over such data export:\n\n - The data export needs to be ordered manually from the Spotify web interface and will take a few days to generate (at which point Spotify sends you an email). In comparison, a single `statify pull` automatically fetches your data.\n - Once you've ordered one data export, it is cached and you cannot order a second one for some time (I don't how much time, since I'm still having my own one cached, and that has been 12 days) meaning you'll need to wait in order to have your data up-to-date. In comparison, assuming you run `statify pull` in a cron, your Statify database is always up-to-date.\n - The data in the data export (in JSON) is way less rich than the data from the Spotify API. Most notably, listenings in the export don't have the Spotify ID of songs listened to, meaning the only way to retrieve the associated rich data from the API would be via a Search, which would not be very reliable.\n - More generally, the data export is in a badly documented format that could be easily broken or updated according to Spotify's internal decisions. The API is well-documented and can be considered stable on a given version.\n\nHere is an example of a listening in the data export from June 2020:\n\n```json\n{\n\t\"endTime\" : \"2020-05-04 17:17\",\n\t\"artistName\" : \"French 79\",\n\t\"trackName\" : \"Diamond Veins (feat. Sarah Rebecca)\",\n\t\"msPlayed\" : 3822\n}\n```\n\nHere is an example of a listening in the Statify database (the relational format is denormalized here in JSON for consistency):\n\n```json\n{\n\t\"track\": {\n\t\t\"id\": \"5G0oVoL309pqsvGDzhMOwx\",\n\t\t\"web_url\": \"https://open.spotify.com/track/5G0oVoL309pqsvGDzhMOwx\",\n\t\t\"name\": \"Diamond Veins (feat. Sarah Rebecca)\",\n\t\t\"cover_url\": \"https://i.scdn.co/image/ab67616d0000b2732cda1a639cd6e26ccfc0773b\",\n\t\t\"preview_url\": \"https://p.scdn.co/mp3-preview/c904f3f3a098bd88cb3359c0c3605dcf0f5b3225\",\n\t\t\"duration\": 240120,\n\t\t\"explicit\": 0,\n\t\t\"iscr\": \"FR9W11161070\",\n\t\t\"popularity\": 58,\n\t\t\"album\": {\n\t\t\t\"id\": \"6rkKOTP3oBns0nR6mfHOsH\",\n\t\t\t\"web_url\": \"https://open.spotify.com/album/6rkKOTP3oBns0nR6mfHOsH\",\n\t\t\t\"cover_url\": \"https://i.scdn.co/image/ab67616d0000b2732cda1a639cd6e26ccfc0773b\",\n\t\t\t\"name\": \"Olympic\",\n\t\t\t\"release_date\": \"2016-10-21\"\n\t\t},\n\t\t\"artists\": [\n\t\t\t{\n\t\t\t\t\"id\": \"6MJKlN8ya42Agsw3iQZs6e\",\n\t\t\t\t\"web_url\": \"https://open.spotify.com/artist/6MJKlN8ya42Agsw3iQZs6e\",\n\t\t\t\t\"name\": \"French 79\"\n\t\t\t}\n\t\t]\n\t},\n\t\"played_at\": \"2020-05-04T17:17:18.254Z\",\n\t\"context\": {\n\t\t\"type\": \"playlist\",\n\t\t\"playlist\": {\n\t\t\t\"spotify_id\": \"56DcZmigEBatLaDqswNKOx\",\n\t\t\t\"web_url\": \"https://open.spotify.com/playlist/56DcZmigEBatLaDqswNKOx\",\n\t\t\t\"cover_url\": \"https://mosaic.scdn.co/640/coverurl\",\n\t\t\t\"name\": \"Electro\",\n\t\t\t\"is_public\": false,\n\t\t\t\"owner_name\": \"Valentin\"\n\t\t}\n\t}\n}\n```\n\nThe rich data from the API that Statify grabs would allow the implementation of a \"Year in review\" webpage in the likes Spotify has been doing for 2019, with only front-end work (I would definitely merge that, if you feel inspired).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoobuzz%2Fstatify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffoobuzz%2Fstatify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoobuzz%2Fstatify/lists"}