{"id":32171322,"url":"https://github.com/arathunku/nimrag","last_synced_at":"2025-10-29T09:50:19.302Z","repository":{"id":232220063,"uuid":"779965267","full_name":"arathunku/nimrag","owner":"arathunku","description":"Use Garmin API from Elixir. Fetch activities, steps, and more!","archived":false,"fork":false,"pushed_at":"2025-07-08T17:57:05.000Z","size":85,"stargazers_count":21,"open_issues_count":4,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-09T04:09:21.469Z","etag":null,"topics":["api","elixir","garmin","garmin-connect"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/nimrag","language":"Elixir","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/arathunku.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-31T09:54:52.000Z","updated_at":"2025-10-05T10:33:11.000Z","dependencies_parsed_at":null,"dependency_job_id":"6fcb0916-fc58-4e66-9207-c212a6d1c4c7","html_url":"https://github.com/arathunku/nimrag","commit_stats":null,"previous_names":["arathunku/nimrag"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/arathunku/nimrag","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arathunku%2Fnimrag","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arathunku%2Fnimrag/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arathunku%2Fnimrag/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arathunku%2Fnimrag/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arathunku","download_url":"https://codeload.github.com/arathunku/nimrag/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arathunku%2Fnimrag/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280307054,"owners_count":26308483,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-21T02:00:06.614Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["api","elixir","garmin","garmin-connect"],"created_at":"2025-10-21T17:48:08.571Z","updated_at":"2025-10-21T17:48:13.795Z","avatar_url":"https://github.com/arathunku.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nimrag\n\n[![Actions Status](https://github.com/arathunku/nimrag/actions/workflows/elixir-build-and-test.yml/badge.svg)](https://github.com/arathunku/nimrag/actions/workflows/elixir-build-and-test.yml) \n[![Hex.pm](https://img.shields.io/hexpm/v/nimrag.svg?style=flat)](https://hex.pm/packages/nimrag)\n[![Documentation](https://img.shields.io/badge/hex-docs-lightgreen.svg?style=flat)](https://hexdocs.pm/nimrag)\n[![License](https://img.shields.io/hexpm/l/nimrag.svg?style=flat)](https://github.com/arathunku/nimrag/blob/main/LICENSE.md)\n\n\u003c!-- @moduledoc --\u003e\n\nUse Garmin API from Elixir. Fetch activities, steps, and more!\n\nNimrag is Garmin in reverse, because we have to reverse engineer the API and auth. ¯\\_(ツ)_/¯\n\n## Installation\n\nThe package can be installed by adding Nimrag to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:nimrag, \"~\u003e 0.1.0\"}\n  ]\nend\n```\n\nIf you'd like to use it from Livebook, take a look at [this example](./examples/basic.livemd) or watch demo:\n\nhttps://github.com/arathunku/nimrag/assets/749393/7246f688-4820-4276-96de-5d8ed7b2fd16\n\n## Usage\n\n### Initial auth\n\nGarmin doesn't have any official public API for individuals, only businesses.\nIt means we're required to use username, password and (optionally) MFA code to obtain\nOAuth tokens. OAuth1 token is valid for up to a year and it's used to generate\nOAuth2 token that expires quickly, OAuth2 token is used for making the API calls. \nAfter OAuth1 token expires, we need to repeat the authentication process. \n\nPlease see `Nimrag.Auth` docs for more details about authentication, \nand see `Nimrag.Credentials` on how to avoid providing plaintext credentials directly in code. \n\n```elixir\n# If you're using it for the first time, we need to get OAuth Tokens first.\ncredentials = Nimrag.Credentials.new(\"username\", \"password\")\n# you may get prompted for MFA token on stdin\n{:ok, client} = Nimrag.Auth.login_sso()\n\n# OPTIONAL: If you'd like to store OAuth tokens in ~/.config/nimrag and not log in every time\n:ok = Nimrag.Credentials.write_fs_oauth1_token(client)\n:ok = Nimrag.Credentials.write_fs_oauth2_token(client)\n```\n\n### General\n\nUse functions from `Nimrag` to fetch data from Garmin's API.\n\n```elixir\n# Restore previously cached in ~/.nimrag OAuth tokens\nclient = Nimrag.Client.new() |\u003e Nimrag.Client.with_auth(Nimrag.Credentials.read_oauth_tokens!())\n\n# Fetch your profile\n{:ok, %Nimrag.Api.Profile{} = profile, client} = Nimrag.profile(client)\n\n# Fetch your latest activity\n{:ok, %Nimrag.Api.Activity{} = activity, client} = Nimrag.last_activity(client)\n\n# Call at the end of the session to cache new OAuth2 token\n:ok = Nimrag.Credentials.write_fs_oauth2_token(client)\n```\n\n### Fallback to raw responses\n\n`Nimrag` module has also functions with `_req` suffix. They return `{:ok, Req.Response{}, client}` and\ndo not process nor validate returned body. Other functions may return, if applicable,\nstructs with known fields.\n\nThis is very important split between response and transformation. Garmin's API may change\nat any time but it should still be possible to fallback to raw response if needed, so that\nany user of the library didn't have to wait for a fix.\n\nAPI calls return `{:ok, data, client}` or `{:error, error}`. On success, client is there\nso that it could be chained with always up to date OAuth2 token that will get\nautomatically updated when it's near expiration.\n\nThere's at this moment no extensive coverage of API endpoints, feel free to submit\nPR with new structs and endpoints, see [Contributing](#contributing).\n\n### Rate limit \n\nBy default, Nimrag uses [Hammer](https://github.com/ExHammer/hammer) for rate limiting requests.\nIf you are already using `Hammer`, you can configure backend key via config.\n\n```elixir\nconfig :nimrag, hammer: [backend: :custom]\n```\n\nBy default, Hammer's ETS based backend will be started.\n\n\u003e #### API note {: .warning}\n\u003e Nimrag is not using public Garmin's API so please be good citizens and don't hammer their servers.\n\nSee `Nimrag.Client.new/1` for more details about changing the api limits.\n\n## Contributing\n\nPlease do! Garmin has a lot of endpoints, some are useful, some are less useful and\nresponses contain a lot of fields!\n\nYou can discover new endpoints by setting up [mitmproxy](https://mitmproxy.org/) and capturing\ntraffic from mobile app or website. You can also take a look at\n[python-garminconnect](https://github.com/cyberjunky/python-garminconnect/blob/master/garminconnect/__init__.py).\n\nFor local setup, the project has minimal dependencies and is easy to install \n\n```sh\n# fork and clone the repo\n$ mix deps.get\n# ensure everything works!\n$ mix check\n# do your changes\n$ mix check\n# submit PR!\n# THANK YOU!\n```\n\n### How to add new API endpoints, fields\n\n1. Add new function in `Nimrag` module, one with `_req` suffix and one without.\n  Functions with `_req` should returns direct `Nimrag.Api` result.\n1. Call `_req` function in `test` env and save its response as fixture.\n\n    Example for `Nimrag.profile/1`:\n\n    ```elixir\n    $ MIX_ENV=test iex -S mix\n    client = Nimrag.Client.new() |\u003e Nimrag.Client.with_auth(Nimrag.Credentials.read_oauth_tokens!())\n    client |\u003e Nimrag.profile_req() |\u003e Nimrag.ApiHelper.store_response_as_test_fixture()\n    ```\n\n1. Add tests for new function in [`test/nimrag_test.exs`]\n1. Define new [`Schematic`](https://github.com/mhanberg/schematic) schema in `Nimrag.Api`,\n  and ensure all tests pass.\n\n## License\n\nCopyright © 2024 Michal Forys\n\nThis project is licensed under the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farathunku%2Fnimrag","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farathunku%2Fnimrag","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farathunku%2Fnimrag/lists"}