A command line interface for the Anytype API, written in Go.
The typed API client is auto-generated from Anytype's official OpenAPI spec
using oapi-codegen, so it stays
in lockstep with the published contract.
Implements auth (POST /v1/auth/challenges and .../api_keys): start a
challenge and exchange the 4-digit code for an API key, spaces management
(list, get, create and update via /v1/spaces and /v1/spaces/{space_id}),
global search (POST /v1/search), types inspection
(GET /v1/spaces/{space_id}/types and .../types/{type_id}), files
management (upload, download and delete via .../files and
.../files/{file_id}), lists (collections/sets): inspect their views and
objects, and add/remove objects via .../lists/{list_id}/..., properties
management (list, get, create, update and delete via .../properties and
.../properties/{property_id}), tags (the selectable values of a
select/multi-select property): list, get, create, update and delete via
.../properties/{property_id}/tags/..., and objects (list, get, create,
update, delete via .../objects and .../objects/{object_id}). The architecture
is set up so more commands (members, …) can be added incrementally.
Requires Go 1.24+.
go build -o anytype-api ./cmd/anytype-api
# optionally: go install ./cmd/anytype-apiThe CLI reads your API token from an environment variable:
| Variable | Required | Default | Purpose |
|---|---|---|---|
ANYTYPE_API_KEY |
yes | — | Bearer token for the Anytype API |
ANYTYPE_API_URL |
no | http://127.0.0.1:31009 |
Base URL of the local Anytype server |
export ANYTYPE_API_KEY="your-token-here"The Anytype desktop app exposes its API locally on 127.0.0.1:31009. Every
request sends the required Anytype-Version: 2025-11-08 header automatically.
# Search every space for "roadmap"
anytype-api search roadmap
# Restrict to specific object types (repeatable), limit results
anytype-api search "launch" --type task --type page --limit 10
# Paginate
anytype-api search roadmap --limit 20 --offset 20
# Machine-readable output for scripting (pipe to jq, etc.)
anytype-api search roadmap --format jsonThe CLI follows the AXI (Agent eXperience Interface) principles so that AI agents can drive it cheaply. Two flags are shared by every command:
| Flag | Default | Description |
|---|---|---|
--format |
toon |
Output format: toon, table, or json |
--full |
false |
Show complete field values instead of truncating long ones |
toon(default) — TOON is a token-efficient, tabular notation (~40% fewer tokens than JSON). List output carries a pre-computedcount: N of M totalaggregate and ahelp[]:block of next-step suggestions.table— human-friendly aligned columns.json— the raw, unabridged API object, for scripting.
# TOON (default): compact, agent-friendly
anytype-api spaces list
# spaces[2]{name,network,id}:
# My space,local,bafyre...
# Notes,local,bafyre...
# count: 2 of 2 total
# help[2]:
# Run `anytype-api spaces get <id>` to inspect one space
# Run `anytype-api spaces create --name "..."` to add a space
# Human table
anytype-api spaces list --format table
# Raw JSON for scripting
anytype-api spaces list --format jsonThe legacy per-command --json flag still works as an alias for
--format json.
skills/anytype-cli/ packages this CLI as an
Agent Skill — a portable
SKILL.md (plus a full command reference) that teaches an AI agent how to
authenticate, resolve ids, and drive every command safely. The format works
across consumers:
- Claude Code discovers it automatically in this repo via the
.claude/skills/anytype-clisymlink. - Claude.ai / Claude Desktop / Cowork: upload the
skills/anytype-clifolder (or a packaged.skillzip of it) as a custom skill. - Claude Agent SDK and other consumers of the open skill format: point
the skills directory at
skills/.
| Flag | Short | Default | Description |
|---|---|---|---|
--type |
-t |
— | Object type to include (repeatable): page, task, … |
--limit |
-L |
100 |
Maximum results to return (max 1000) |
--offset |
0 |
Results to skip (for pagination) | |
--format |
toon |
Output format: toon, table, json |
|
--full |
false |
Show complete field values without truncating |
File-layout objects (file, image, video, audio, pdf) are excluded by default;
pass the corresponding --type to include them.
# Content-first: the bare command lists your spaces
anytype-api spaces
# Add extra columns to the minimal default schema
anytype-api spaces list --fields name,description,gateway,id| Flag | Short | Default | Description |
|---|---|---|---|
--fields |
name,network,id |
Columns to include (list) |
|
--limit |
-L |
100 |
Maximum results to return (max 1000) |
--offset |
0 |
Results to skip (for pagination) |
Available --fields: name, network, id, description, gateway.
Types (Page, Task, Bookmark, …) are scoped to a space, so every subcommand
requires a --space id.
# List every type defined in a space
anytype-api types list --space bafyre...
# Show one type's details
anytype-api types get bafyre...type-id --space bafyre...
# Machine-readable output
anytype-api types list --space bafyre... --format json
# Create a type with convenience flags
anytype-api types create --space bafyre... \
--name Task --plural Tasks --layout basic --icon ✅
# Create a type from a JSON payload (file or stdin)
cat type.json | anytype-api types create --space bafyre... --file -
# Update (rename) a type — only supplied fields change
anytype-api types update bafyre...type-id --space bafyre... --name "New name"
# Delete (archive) a type, with confirmation
anytype-api types delete bafyre...type-id --space bafyre...
# Delete without prompting (for scripts)
anytype-api types delete bafyre...type-id --space bafyre... --yes| Flag | Short | Default | Description |
|---|---|---|---|
--space |
-s |
— | Space id to operate on (required) |
--limit |
-L |
100 |
Maximum results to return (list) |
--offset |
0 |
Results to skip (list) |
|
--file |
-f |
— | JSON payload file, - for stdin (create, update) |
--name |
— | Type name (create, update) |
|
--plural |
— | Plural type name (create, update) |
|
--key |
— | Type key in snake_case (create, update) |
|
--layout |
— | Layout: basic, note, profile, action |
|
--icon |
— | Emoji icon for the type (create, update) |
|
--yes |
-y |
false |
Skip the confirmation prompt (delete) |
--format |
toon |
Output format: toon, table, json |
|
--full |
false |
Show complete field values without truncating |
The type definition for create/update can come from a --file JSON payload
(matching the API's CreateTypeRequest/UpdateTypeRequest shape), from the
convenience flags, or both. When combined, flags take precedence over fields in
the payload, so a file can serve as a template you tweak per invocation.
Files are scoped to a space, so every subcommand requires a --space id.
# Upload a local file
anytype-api files upload ./photo.png --space bafyre...
# Machine-readable output for the uploaded file object
anytype-api files upload ./photo.png --space bafyre... --format json
# Download a file to a sensible local filename
anytype-api files download bafyre...file-id --space bafyre...
# Download to a specific path
anytype-api files download bafyre...file-id --space bafyre... --output photo.png
# Stream to stdout for piping (also the default when stdout is not a terminal)
anytype-api files download bafyre...file-id --space bafyre... --output - > photo.png
# Delete (move to bin), with confirmation
anytype-api files delete bafyre...file-id --space bafyre...
# Delete permanently, without prompting (for scripts)
anytype-api files delete bafyre...file-id --space bafyre... --skip-bin --yes| Flag | Short | Default | Description |
|---|---|---|---|
--space |
-s |
— | Space id to operate on (required) |
--output |
-o |
— | Destination path, - for stdout (download) |
--skip-bin |
false |
Permanently delete instead of the bin (delete) |
|
--yes |
-y |
false |
Skip the confirmation prompt (delete) |
--format |
toon |
Output format: toon, table, json |
|
--full |
false |
Show complete field values without truncating |
download writes to --output when given (- means stdout). Without
--output, it streams to stdout when that is not a terminal (so the command
pipes safely), otherwise it writes to a file named after the file id with an
extension inferred from the response media type.
Lists (collections and sets) are scoped to a space, so every subcommand requires
a --space id. Find a list id by searching the space for objects of type
collection or set.
# List the views defined for a list
anytype-api lists views bafyre...list-id --space bafyre...
# List the objects in a view (filtered and sorted by that view)
anytype-api lists objects bafyre...list-id --space bafyre... --view 67bf3f21...
# Add one or more objects to a collection
anytype-api lists add bafyre...list-id --space bafyre... bafyreA... bafyreB...
# Remove an object from a collection, with confirmation
anytype-api lists remove bafyre...list-id bafyreA... --space bafyre...
# Remove without prompting (for scripts)
anytype-api lists remove bafyre...list-id bafyreA... --space bafyre... --yes
# Machine-readable output
anytype-api lists views bafyre...list-id --space bafyre... --format json| Flag | Short | Default | Description |
|---|---|---|---|
--space |
-s |
— | Space id the list belongs to (required) |
--view |
— | View id to filter/sort by (required, objects) |
|
--limit |
-L |
100 |
Maximum results to return (views, objects) |
--offset |
0 |
Results to skip (views, objects) |
|
--yes |
-y |
false |
Skip the confirmation prompt (remove) |
--format |
toon |
Output format: toon, table, json |
|
--full |
false |
Show complete field values without truncating |
Only collections can be modified with add/remove; the objects of a set are
determined by its query.
api/
openapi.yaml # Vendored Anytype OpenAPI spec (source of truth)
oapi-codegen.yaml # Generator config (scoped to the Search, Types and Lists tags)
internal/
api/ # Auto-generated client + models (do not edit by hand)
anytype/ # Thin wrapper: env config, auth, request helpers
# client.go holds the shared Client; per-resource
# methods live in client_<resource>.go
cmd/anytype-api/ # Cobra CLI commands
After updating api/openapi.yaml (or widening the include-tags in
api/oapi-codegen.yaml to expose more endpoints):
go generate ./...The generator is pinned as a Go tool dependency, so no separate install is needed.
internal/api/anytype.gen.go is fully derived from the spec, so it should never
be merged textually. .gitattributes marks it merge=ours; enable that driver
once per clone:
git config merge.ours.driver trueAfter rebasing a feature branch, regenerate rather than resolving conflicts in the generated file by hand:
go generate ./...