Skip to content

How To: Export AirTags With The CLI

Shane B edited this page Aug 16, 2026 · 7 revisions

Exporting AirTags from the command line 💻

The same exporter as the wizard, without a window. Same questions, same zip.

Use the wizard unless you have a reason not to. Reasons people do have: a machine with no screen, a server or a container, wanting it in a script, or wanting to see exactly what it is doing.


cli-choose-accessories

Getting it

There is no download for the CLI — it runs from a clone:

git clone https://github.com/parawanderer/OpenTagViewer.git
cd OpenTagViewer/python
uv sync

uv fetches a suitable Python itself, so you do not need one installed. Without uv, install what pyproject.toml lists into a virtualenv of your own.

Running it

uv run python -m exporter.cli --no-password --output my-tags.zip

Important

Use --no-password for now. The CLI encrypts bundles by default, and no released version of the Android app can open an encrypted zip — its reader cannot decrypt anything at all. Without the flag you get a file your phone will refuse.


What happens

It prints which route it is taking and why, then asks:

On macOS 14 or older it reads that Mac's own Find My files. macOS asks for your login password twice, in its own prompt, and that is the whole of it — no Apple ID, no verification code, and nothing added to your Apple account.

Everywhere else it signs in to iCloud:

  1. Your Apple ID and password. Used once, saved nowhere.
  2. A verification code, by SMS or trusted device.
  3. The screen-lock passcode of one of your Apple devices — its PIN, or a Mac's login password. Not your Apple ID password. This is the step people get wrong; if it is refused, try another device from the list.

Then it shows what it found:

? What should go in the bundle?  (space to select, a for all, enter to confirm)
❯ ◯ 🐈 Shane's Wallet    AirTag, serial …, paired 2025-02-01
  ◯ 🎒 Shane's Backpack  AirTag, serial …, paired 2025-02-02
  ◯ unnamed              iPad (iPad13,1), paired 2025-02-03
  ◯ Shane's MacBook Air  Mac (Mac16,1), paired 2025-02-04 - no alignment record: slow first locate

Arrow keys to move, space to tick, a for all, enter when done. Nothing is ticked to begin with, deliberately — see the warning below.

If your output is piped somewhere, or there is no terminal to draw on, it falls back to a numbered list you type numbers at. Same questions.

Warning

Exporting a tag cannot be undone. The zip holds its keys, and the only way to revoke them is to unpair the AirTag. Anyone who ends up with the file can locate it from any Apple account. a selects your own iPhones and Macs too — the CLI names them and asks again if you do that, because a bundle holding your MacBook lets whoever receives it locate you.

Finally it writes the zip and tells you where. Move it to your phone and import it.


Options

Option
-o, --output Where to write. Defaults to a timestamped zip in the current directory
--no-password Write a plain zip. Required for any released version of the app
--add-keys FILE Add a self-generated tag — OpenHaystack, Macless Haystack, FindMy.py or a plain list of keys. Repeatable
--source auto, icloud, local or none. auto uses this Mac's own files where it can; none reads no account at all, for a bundle of nothing but --add-keys tags
--source-user What the recipient sees as "exported by". Defaults to your machine's username
--accept-terms Agree to pending iCloud terms without displaying them
--anisette-url Use a remote Anisette server instead of running one locally
-v / -vv Show what it is doing. -vv includes the CloudKit and keychain steps — this is what to attach to a bug report, after reading the warning below

Caution

Read -v or -vv output before you paste it anywhere, and strip anything that identifies you. It is written for debugging, not for publishing, and it names real things:

  • Your devices, by name, model and serial number. Escrow records are described the way a person recognises them, so a line reading "Paula's iPhone, iPhone14,2, serial F2L…" is normal.
  • Keychain item attributes, exactly as Apple stores them, including their account and label fields.
  • Peer identifiers for every device in your account's trust circle. Pseudonymous, but stable and unique to you.

No key, password or passcode is logged, and payloads appear only as byte counts. But nobody can promise a given identifier never reaches the output: the text comes from a library reading Apple's own structures, and a field that is harmless on one account may not be on another. The CLI prints this same warning, in red, whenever you turn logging on.


Running it from a script ⚙️

Important

Sign in by hand at least once first. That run registers this machine as a device on your Apple account and remembers it, so later runs usually go straight through with no verification code. Until it has happened the run cannot be unattended — Apple sends a code to your phone and something has to type it.

After that, every other answer can be supplied up front:

export OPENTAGVIEWER_APPLE_PASSWORD='...'
export OPENTAGVIEWER_DEVICE_PASSCODE='...'

uv run python -m exporter.cli \
  --non-interactive \
  --apple-id me@example.com \
  --device F2LX9Q \
  --all-tags \
  --no-password \
  --output tags.zip

--non-interactive is the important one. Without it, a missing answer is prompted for — so a scheduled job sits waiting on a keystroke that never comes, producing no error and no output and never finishing. With it, the run fails and names the question it wanted to ask.

Where to put the password

There is no --password flag, on purpose. Anything on a command line can be read by every other user on the machine with ps, and it goes into your shell history.

A file, or a pipe 🥇 --password-file secret.txt, or --password-file - to read a line from standard input. chmod 600 it — a file others can read is refused
An environment variable 🥈 OPENTAGVIEWER_APPLE_PASSWORD, OPENTAGVIEWER_DEVICE_PASSCODE. Good enough for a CI secret store. Weaker than a file: readable through /proc by your own processes on Linux, inherited by children, and setting it inline on a command puts it in history anyway
Being asked 🥉 What happens when neither is given

Set the variable from a secret store or read -s, not from a literal in a script you commit. In most shells, starting a command with a space also keeps it out of history.

The flags

Option
--apple-id Sign in as this
--password-file Apple ID password, from a file or - for standard input
--passcode-file The device screen-lock passcode, the same way
--device SERIAL Unlock with this device instead of picking from a list. Fails and lists what exists if nothing matches
--all-tags Export everything, instead of choosing
--include-my-devices Let --all-tags include your own iPhones, iPads and Macs
--non-interactive Fail rather than ask

Warning

--all-tags leaves your own devices out deliberately. A bundle holding your MacBook lets whoever receives it locate you. Interactively the CLI names each one and asks again; a scheduled run has nobody to ask, so it exports the tags and prints what it skipped.

0 means everything asked for was exported. 1 means something was not — including a run that wrote a bundle but could not read the account, so do not treat "a file exists" as success.


When it does not work

"Signing in got as far as your account and then stopped" — usually unaccepted iCloud terms. Apple takes agreement on one of its own devices or at iCloud.com and nowhere else, so the CLI offers to show them: the documents are rendered as text and opened in your pager, and nothing is sent unless you type ACCEPT. Press q to leave the pager.

"Apple would not accept that sign-in" — if that appeared after a verification code, check both the code and the password. A code expires quickly, and Apple reports either as a credentials failure at that point.

"No record on this account can currently be recovered from" — either there is no device enrolled that can be recovered from, or the service is having a bad day. The CLI says which it thinks it is; worth retrying later.

It sits on one tag for minutes — that tag has no key-alignment record, so it is searching its whole key history. Expected, and nothing can be done from this side.


What it leaves behind

Nothing on macOS 14 or older, which never signs in.

Everywhere else, one entry in your Apple device list: a MacBook Pro on macOS 13.4.1, serial 0PENTAGXPORT. That is this program — the model and OS come from the library it uses, and the serial is the exporter's own. One entry however many times you export.

Remove it whenever you like at account.apple.comDevicesRemove from Account, from any browser and with no Apple device. It is not a trusted device and cannot receive verification codes, so removing it cannot lock you out — see what it looks like.

On disk it leaves nothing at all: no account file, no cached keys, no saved passcode.

Clone this wiki locally