Configuration guide

Map each app to a stable putiorr profile.

Configure one RR profile per app, attach the right download profile, then paste the generated Transmission values into Radarr, Sonarr, and Lidarr. Browser grabs get a profile of their own.

RR profiles

Each RR profile maps one app to a put.io destination folder, a shared local download target, and a Transmission RPC endpoint path. The app's download-client category creates the final import subfolder under that target.

A download belongs to exactly one profile, and the owner is decided once — when the download is created — and never re-derived. For an *arr app that is the profile the request named: the RPC path it arrived on, or the app's own User-Agent. For a browser grab it is the profile the caller picked, else the one claiming the page's site, else the one set to take the sites nobody claims. The category selects nothing: it names the staging subfolder and never picks or vetoes a profile.

Every *arr can share the /transmission/rpc endpoint with no URL Base change. Sonarr, Radarr, Lidarr, Readarr and Prowlarr each put their own name in the User-AgentSonarr/4.0.19.2932 (linux x64) — and putiorr matches the part before the slash against each profile's slug, name and app preset. The header routes the request and nothing else; it never moves an existing download between profiles.

A distinct RPC path is the override for what the name cannot settle: two Sonarr instances, or a client that does not name itself. That path always wins over the header. A request neither resolves is refused for torrent-add and torrent-remove, naming the RPC path each profile should use instead — including the profile still sitting on the shared path, which needs one of its own. session-get and torrent-get always answer: the first is the call an *arr uses to test the connection, the second is the queue poll that drives completed-download import, and an unresolved one lists every download rather than failing.

One put.io transfer belongs to one download, keyed on put.io's own transfer id. put.io de-duplicates, so a second profile grabbing a release the first already owns gets that same transfer back, and putiorr refuses the second add by name rather than letting two profiles share one download.

App put.io folder Download folder RPC endpoint Category
Radarr putiorr /downloads /radarr/transmission/rpc radarr
Sonarr putiorr /downloads /sonarr/transmission/rpc sonarr
Lidarr putiorr /downloads /lidarr/transmission/rpc lidarr
Readarr putiorr /downloads /readarr/transmission/rpc readarr

Example: Sonarr category sonarr plus download folder /downloads means completed files appear under /downloads/sonarr for Sonarr's importer. The same host folder must be mounted into putiorr and Sonarr at /downloads, for example /volume/downloads:/downloads in both containers, so Sonarr does not need custom Remote Path Mapping.

Where the files land

<download folder>/<category>/<the name put.io gave the transfer>

The name is put.io's, untouched, because every *arr resolves a completed download as downloadDir + name: a folder spelled any other way is a download that never imports. That folder is frozen the first time the download is staged, so a later rename on put.io does not move it — the name putiorr reports follows the rename, because that is what you see in the dashboard, but the files on disk do not.

Switching a profile off

A disabled profile accepts no new work. It is a refusal, never a disappearance: it still holds its RPC path, still answers to its own name on the shared endpoint, still claims its browser sites and the catch-all if it holds that, and is still counted when the shared endpoint decides whether it is ambiguous. Only the doors that create work ask — torrent-add, /api/grab, and the adoption of a put.io transfer putiorr did not create. The first two refuse with one sentence naming the profile, because somebody is waiting on the answer; adoption is nobody's request, so the transfer is left on put.io without a word and is adopted on the next poll once the profile is switched back on. The downloads already queued keep downloading and stay listable, importable, and removable.

Deleting a profile

A download cannot be left without an owner, so a profile that still owns downloads is not deleted until you say what happens to them. The dialog states the counts first — how many downloads, how many of them already removed from the list but still on put.io, and how many files and bytes are in their staging folders — then takes one of three answers: move them to another RR profile, remove them from putiorr (optionally cancelling their put.io transfers and deleting their files), or neither, which is refused.

The move target is restricted to profiles that download into the same folder. Nothing moves on disk, so a profile that stages elsewhere would be pointed at an empty directory — and a finished download whose files have gone missing is deleted and its put.io transfer cancelled.

Download profiles

Download profiles tune local-copy behavior by use case. Use one profile per download shape when movies, episodes, music, or books need different reset rules.

Startup grace

Gives large transfers time to reach normal throughput before slow-speed checks begin.

Ignore below

Excludes tiny files from slow-speed reset rules so metadata does not distort behavior.

Slow threshold

Restarts a local file download after it stays below the configured speed for the configured duration.

Radarr, Sonarr, and Lidarr

Add a Transmission download client in each app. When the app container shares a Docker network with putiorr, the host is the compose service name and the port is putiorr's internal RPC port.

Field Radarr Sonarr Lidarr
Name putiorr putiorr putiorr
Host putiorr putiorr putiorr
Port 9091 9091 9091
Use SSL Off Off Off
Category radarr sonarr lidarr
Directory /downloads /downloads /downloads
URL Base /radarr/transmission /sonarr/transmission /lidarr/transmission

Leave username and password blank unless you configured PUTIORR_RPC_USERNAME and PUTIORR_RPC_PASSWORD. Keep completed-download handling enabled in the *arr app.

Prowlarr

Prowlarr usually does not need its own putiorr profile. It connects to Radarr, Sonarr, and Lidarr, syncs indexers, and those apps send accepted grabs to putiorr through their Transmission client settings.

Radarr URL: http://radarr:7878
Sonarr URL: http://sonarr:8989
Lidarr URL: http://lidarr:8686

Rejecting junk releases

A Sonarr or Radarr profile can check what put.io actually produced before putiorr downloads any of it. A release the app could never import is blacklisted, searched again, and removed from put.io — rather than downloaded, failed at import, and left blocking that episode or movie in Activity.

This needs the app's URL and API key. Blacklisting a release — Sonarr and Radarr call it the blocklist — exists only in their REST API: a download client speaking Transmission has no way to ask for it, which is why putiorr sets a torrent error today and the item still sits in the queue.

Open the profile wizard on a Sonarr or Radarr profile and fill in the Reject bad releases step.

The putiorr profile wizard on a Sonarr profile, showing the Reject bad releases step with the rejection switch ticked, an App URL of http://sonarr:8989, a blank API key field, a zero minimum release size, and a 90 day rejection log retention.
FieldWhat it does
Reject releases… The switch for the whole feature. Off by default, and off after upgrading.
App URL Where the app answers, for example http://sonarr:8989 or http://radarr:7878.
App API key From the app's Settings → General. Stored write-only: putiorr never sends it back to the browser, and leaving the field blank keeps the stored one.
Minimum release size Optional floor in MB. 0 disables it.
Keep the rejection log for Days to keep this profile's rejections. 0 keeps them forever.

What counts as junk

  • Nothing the app can import. Decided per app rather than by one shared list, so an audio-only release or a lone PDF is refused by Sonarr and Radarr even though some other app could import it.
  • Still packed. Neither Sonarr nor Radarr extracts an archive from a torrent download, so a .rar set is exactly the "no files found are eligible for import" case.
  • Far short of what was announced. put.io finished but produced less than half the size the torrent claimed.
  • Below the minimum size, when one is set.

Disc structures — VIDEO_TS, BDMV — and .iso files still pass, and so does anything sitting next to a playable file.

Leave this off if you run Unpackerr or any other extractor. It makes packed releases importable after all, so putiorr would blacklist perfectly good ones. A blacklisting cannot be undone from putiorr.

The minimum size is off on purpose

A figure that is safe for movies is not safe for television. A half-hour SD episode is 150–250 MB and 720p anime runs 200–350 MB, so a shipped default would quietly blacklist entire libraries. Set it per profile if you want it, and set it low on Sonarr.

Searching again

putiorr asks the app to search for a replacement itself rather than relying on the app to do it. Sonarr only re-searches after a failure when its own Redownload Failed setting is on, so leaving that decision to the app meant releases were blacklisted and then never replaced, with nothing saying why. However that setting is configured, exactly one search runs.

The rejection log

Every rejection is recorded on the dashboard's Rejected releases screen: what was thrown away, why, on which profile, and when. It is paged, searchable, and filterable by outcome, and unread rejections raise a badge in the sidebar until Read all clears them. A blacklisting is invisible and permanent, so this is the only place a wrong one can be found after the fact.

Releases putiorr judged unimportable but could not report to the app — it was unreachable, or its queue never held the release — are listed separately as Not blacklisted. Those were downloaded as usual, and their queue item may still need attention.

Lidarr and Readarr are not offered this step. They serve a different API version that putiorr does not implement.

Browser grabs

The putiorr browser extension, in the extension/ folder of the putiorr repository, captures the magnet: links and .torrent downloads you click in Chrome and sends them to putiorr, which queues them on put.io and copies the finished files to the local download folder. No *arr app is involved, so these grabs get their own kind of profile.

In the putiorr dashboard, add a profile and set App preset to Putiorr Grab. The wizard then hides the RPC endpoint step, because nothing connects to a grab profile over Transmission RPC, and shows a Browser grabs step instead.

Field What to enter
Display name The name the extension shows in its profile list and right-click menu, such as movies.
Put.io destination folder putiorr, or a separate folder to keep browser grabs apart on put.io.
Shared download folder Where the files land, such as /downloads. Grabs are written straight into it; no category subfolder is created.
Download profile The local-copy behavior to apply, exactly as for an *arr profile.
Browser sites Comma-separated sites whose grabs land here, such as x.example, z.example. A plain entry matches that host and nothing under it; put *. in front — *.x.example — to match it and every subdomain of it, at any depth. The star is only ever the first thing in an entry.
Take grabs from any site no other profile claims Ticked on one grab profile, every grab from a site nobody listed lands there. A fallback, not a wildcard: a profile that lists a site still wins for it. At most one profile may hold it, and a second save is refused, naming the one that does — with an offer to hand the role over, which saves what you typed and unticks the box on that profile in the same step.

putiorr resolves every grab itself, and nothing in the extension takes part in that decision. It answers in a fixed order: the profile picked by hand from the right-click menu, then the profile claiming the page's host exactly, then the profile with the most specific wildcard covering it — the longest base wins, so *.dl.x.example beats *.x.example — then the profile that takes the unclaimed sites, and otherwise a refusal. Matching compares the hostname alone; scheme, port, and path are stripped first. A grab aimed at a profile that is not a Putiorr Grab profile is refused by name. A grab profile has no Transmission RPC endpoint at all — nothing connects to one as a download client — so /api/grab is the only way in, and it requires the X-Putiorr-Grab header.

Two profiles may not hold the same entry, and that save is refused naming the one that already has it. Coverage that merely overlaps is intended: dl.x.example on one profile and *.x.example on another sends that one host to the first and the rest of the domain to the second, which is what the order above is for.

The extension's toolbar icon opens a popup about the page in the current tab. It names that page's site and says which profile a grab from it would land in — the profile that claims it, the profile that takes the sites nobody claims, or nobody at all — and, when no profile claims it, offers to claim it for one of them. The site it stores sits in an editable field, pre-filled with the page's hostname exactly as it is, www. and all, and the popup says to start it with *. to take the subdomains too. Nothing here shortens a host to the registrable domain behind it: neither putiorr nor the extension carries a public-suffix list, and the rule that would turn www.x.example into x.example would also turn x.co.uk into co.uk.

A site another profile already claims is not offered a second time: the popup names that profile and stops. Moving a site is two profiles changing at once, from a popup that shows one of them, which belongs on the profiles themselves. The popup writes through POST /api/profiles/<id>/browser-sites, which appends one site to one Putiorr Grab profile, normalizes it exactly as Browser sites does, answers with that profile's resulting list, and refuses with 409 — naming the profile — when another one already claims the site. It requires the same X-Putiorr-Grab header /api/grab does.

Switching a grab profile off does not release its sites, or the catch-all if it holds it. It still claims them, and such a grab is refused by name rather than falling through to the next profile, which would put the transfer in a folder nobody chose.

The extension goes on the machine running Chrome — putiorr is on the NAS, the browser is not:

  1. Install putiorr grab from the Chrome Web Store. To load it unpacked instead — to read what you are running, or to pin a version — download putiorr-grab-<version>.zip from the Assets of the newest release, unpack it somewhere permanent, then enable Developer mode on chrome://extensions and choose Load unpacked on the folder holding manifest.json.
  2. Open the extension options and enter the putiorr URL, such as http://nas:9091, plus the Basic auth credentials if putiorr uses them.
  3. Click Test connection & load profiles and press Save. The card lists what putiorr routes to each profile, including which one takes the sites no profile claims.
  4. On a tracker page, click the extension's toolbar icon to see which profile a grab from that site would land in, and to claim the site for one of them.

The browser extension guide covers each of those in full: both install routes, why an unpacked extension's folder path is its identity, and how updating differs between a store install and an unpacked one.

The extension only ever lists Putiorr Grab profiles, so create at least one before loading profiles there. The preset also enables auto-remove completed — in the wizard, through POST /api/profiles, and in PUTIORR_PROFILES_JSON alike: nothing imports a browser grab, so putiorr drops the finished transfer from its list and from put.io once the files are on disk. Send auto_remove_completed: false to opt out.

Progress model

putiorr reports progress in two phases. The dashboard is the best view for file-level local progress and per-file speed.

0-50%:   put.io remote transfer
50-100%: local download from put.io to the download target

Local downloads are written as .part files and resume with HTTP range requests after restarts. Optional slow-speed reset closes and resumes a local download if it stays below the selected threshold after the startup grace window.