Skip to main content

da-cli

CI Python 3.10+ License: MIT Code style: ruff Type checked: mypy Coverage: 92%+ Status: Beta Dependencies: 0 runtime

Sync your DeviantArt gallery to a local folder from the command line — a backup of the art you watch, kept current. Zero runtime dependencies: the whole tool is the Python 3.10+ standard library. A local SQLite index means a re-run costs one API call when nothing new was posted, and launchd (macOS) or a systemd timer (Linux) keeps it running unattended. Plus search and browse helpers.

New to da-cli? Follow the Setup Guide — it walks you through everything from install to first sync in about 10 minutes, with screenshots. Status: Beta — the sync and search flows are stable and covered by 868 tests. macOS Keychain integration is production-ready; Linux Secret Service support is planned. See CHANGELOG.md.

Documentation

I want to… Page
install it and download my first art Getting started
understand a command properly Command guides
look up a command or a flag Command reference
fix an error I just got Troubleshooting
sync automatically every day Scheduling
change a setting Configuration
script it or monitor it Scripting
know what it does with my OAuth secret Security model
contribute CONTRIBUTING · ARCHITECTURE

Full index: docs/

Quick tour

Each group below has a page documenting every flag, what it writes and how it behaves on a second run — linked after the block.

da --version                            # print version
da auth                                 # one-time browser-based login
da auth logout                          # forget local tokens (DA-side authorisation persists; revoke at deviantart.com)
da whoami                               # confirm token + identity
da refresh                              # force-refresh the access token

da sync feed                            # incremental: pull new content from your watch feed
da sync feed --no-mature                # ...excluding mature content (included by default)
da sync artist <username>               # walk one artist's full gallery
da sync watched                         # walk every watched user's gallery
da sync watched --via-feed              # discover artists via watch feed (works with `browse` scope alone)
da sync feed --jitter 0.4               # randomise sleeps by ±40% — avoids burst patterns

da search tag nature                    # browse by tag
da search topic digitalart              # browse a curated DA topic
da search topics                        # list all valid topics
da search user deviantart               # resolve a username
da daily 2026-01-15                     # daily-deviation picks for a date

da user profile <username>              # who is this person
da deviation show <deviationid>         # full metadata for one deviation
da deviation morelikethis <id>          # related deviations via DA's recommender
da watch list                           # show who you watch (needs `user` scope)

da config show                          # inspect config + resolved paths
da config path                          # just the file locations
da config set <key> <value>             # store; secrets go to Keychain on macOS
da config get <key> [--unmask]          # read back (secrets masked unless --unmask)
da config unset <key>                   # remove

da index show                           # synced-index stats (rows, top artists)
da index rebuild                        # walk dest, re-import (idempotent)
da diagnose                             # end-to-end health check
da bench                                # synthetic sync benchmark (no network)

da search popular and da search newest were retired (DA dropped the underlying endpoints). Use da search topic <name>, da search tag <tag>, or da daily instead — both subcommands now exit 2 with an actionable hint.

Read more, by area:

  • Authentication — da auth, auth logout, auth status, whoami, refresh. The PKCE flow, scopes, and the 90-day refresh-token ceiling.
  • Syncing art — sync feed, sync artist, sync watched. What each mode walks, the checkpoint, resuming an interrupted backfill, pacing, and time budgets.
  • Searching and browsing — search tag, search topic, daily. Read-only; downloads nothing.
  • Inspecting users and deviations — user profile, deviation show, watch list. How to find the id or username a sync command needs.
  • Configuration commands — config show, set, get, unset. Where each setting lives and which source wins.
  • Index, health and benchmarking — index show, index rebuild, diagnose, bench. How to tell whether a scheduled run is quietly failing.

Install

da-cli is a stdlib-only Python package with no runtime dependencies. Two install paths:

Option A — git clone + shim (zero global state, recommended for developers):

git clone https://github.com/FZ2000/da-cli.git ~/Documents/da-cli
cd ~/Documents/da-cli
./install.sh                           # copies da + the dacli package to ~/.local/share/da-cli/ and symlinks ~/.local/bin/da → that copy

Option B — pip install. The distribution is named da-sync; the command it installs is da.

pipx install da-sync                   # isolated venv, recommended
da --version

pip install da-sync works too, but pipx gives the tool its own environment, which is what you want for something that puts a command on your PATH.

Two neighbouring PyPI names are not this project: dacli (an unrelated data-engineering tool) and da-cli (unregistered — PyPI rejects it as too similar to dacli, which is why this ships as da-sync). Since this package installs a da command and handles DeviantArt OAuth tokens, check the name.

Python 3.10+ required (uses argparse.BooleanOptionalAction and X | None syntax). No third-party runtime dependencies.

First-time setup

New? Follow the Setup Guide instead — it has screenshots and walks through every step from zero.

Quick version for experienced users:

  1. Create a Confidential OAuth app at https://www.deviantart.com/developers/ → Register Your Application. Set the Redirect URI Whitelist to https://localhost:8765/ (exactly — trailing slash matters). Manage your apps at https://www.deviantart.com/studio/apps.

  2. Configure da-cli:

    da config set client_id 12345
    da config set client_secret <YOUR_SECRET>
    da config set destination ~/Pictures/DA
    da auth
    da whoami
    

The CLI auto-refreshes the access token before each call; you only repeat da auth if you revoke the app or the refresh token expires (every 90 days — da diagnose warns 14 days before).

Scheduled sync

./install_schedule.sh              # macOS: daily at 03:00 via launchd

On macOS this installs a launchd user agent; the script writes the plist and loads it. On Linux, use a systemd user timer or cron. Both are covered, with the Full Disk Access step macOS needs and the enable-linger step systemd needs, in docs/guides/scheduling.md.

Troubleshooting

da diagnose checks configuration, credentials, the destination folder, the index, and the scheduled job, and tells you what is wrong.

For specific errors — a redirect-URI mismatch, an expired refresh token, a scheduled run that silently does nothing — see docs/guides/troubleshooting.md.

Security model

This is documented separately in docs/explanation/security.md; to report a vulnerability see SECURITY.md. Short version:

  • Secrets (client_secret) live in macOS Keychain (or, on other platforms, in a 0600-permissioned config file). Never in this repo, never in git history, never in command-line arguments visible to ps.
  • Tokens (access + refresh) live in ~/.local/state/da-cli/state.json (0600). Refreshing happens automatically before every API call.
  • Network: all DA traffic is HTTPS. Image downloads from wixmp CDN are also HTTPS.
  • PKCE is mandatory: the CLI generates a fresh PKCE code_verifier/code_challenge pair for every da auth run. The verifier never leaves your machine; only the SHA-256 hash is sent in the auth URL.
  • No telemetry, no third-party code at runtime. Stdlib only — urllib, argparse, json, hashlib, subprocess (for the macOS security keychain helper).
  • .gitignore excludes config.json, state.json, sync.log, *.log, and *.tmp — secrets-bearing files cannot be committed by accident.

Examples

The examples/ directory has runnable recipes: a post-sync webhook, a cron health check, a CSV export, and a topic browser that lists what a curated DA topic holds. Each is a short shell or Python script you can copy and edit.

Contributing

Style: stdlib only at runtime. If a feature would need a third-party library, open an issue first. See CONTRIBUTING.md for the lint/type/test bar (every change must pass ruff check, ruff format --check, mypy dacli, and pytest with ≥92 % coverage). See ARCHITECTURE.md for a map of the package.

The Makefile wraps the four-command pipeline:

make dev-setup    # one-time venv + dev-deps install
make check        # ruff + ruff format --check + mypy + pytest (what CI runs)

Pre-commit hooks mirror CI locally — install once with pip install pre-commit && pre-commit install, then every git commit runs ruff / mypy / markdownlint / gitleaks. Config in .pre-commit-config.yaml.

License

MIT — see LICENSE.

Metadata

Release files for da-sync 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for da-sync 0.1.1
File Size Uploaded Provenance Actions
da_sync-0.1.1.tar.gz 224.9 kB With provenance Details for da_sync-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for da-sync 0.1.1
File Interpreter ABI Platform Actions
da_sync-0.1.1-py3-none-any.whl Python 3 none any Details for da_sync-0.1.1-py3-none-any.whl

Total release size: 317.4 kB

Release files / da_sync-0.1.1.tar.gz

Download URL da_sync-0.1.1.tar.gz
Size 224.9 kB
Tags Source
SHA-256 checksum
How to use checksums
c3af31322f3990e4d8af534bb7c45fdd4ee7858d2a6d6ae9c3790a2c51ab7bb0
BLAKE2b-256 checksum
How to use checksums
640ffb14c7347e4f32597128e7f1caffad65cf135e493a21ad96a5a1a4d722df
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 1, 2026.

Transparency log

Release files / da_sync-0.1.1-py3-none-any.whl

Download URL da_sync-0.1.1-py3-none-any.whl
Size 92.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
42022ee44e5823424e506efa09e8cd95a0f2221520d643c709ac82fecca86b61
BLAKE2b-256 checksum
How to use checksums
b9c9c382d3240b34ac04880c87b75e73d2693181361cf95a0fba9c7d88a50be5
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 1, 2026.

Transparency log

Security for da-sync 0.1.1

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for da_sync-0.1.1.tar.gz

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 1, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page