klaviyo-cli
A command-line interface for Klaviyo: campaigns, segments, flows, metrics, and scheduling. Built for humans and AI agents.
uvx --from klaviyo-cli klaviyo --help
No install needed. Or install it: pipx install klaviyo-cli or uv tool install klaviyo-cli. Requires Python 3.11+.
Built and maintained by BS&Co, a retention marketing agency for eCommerce brands. Read why we built it and how we made it safe to hand to AI agents: https://bsandco.us/blog-post/klaviyo-cli
Unofficial. Not affiliated with, endorsed, or supported by Klaviyo, Inc.
Quickstart
export KLAVIYO_API_KEY=pk_...
Get a private API key from Klaviyo under Settings > API Keys. Read commands need read scopes; commands that change data (patch, schedule, create, upload) need write scopes.
klaviyo list-campaigns --days 30
klaviyo account-health
Using with Claude Code and AI agents
--json is a global flag: put it before the command name for machine-readable output on any command:
klaviyo --json list-campaigns --days 30
Deletes are gated. The raw api passthrough can only delete templates (transport.py, DELETE_ALLOWED_PATHS). Segments, lists, campaigns, and flows can only be deleted with klaviyo delete <type> <id>, which prints what it is about to remove (profile count, status, and for segments and lists every flow and draft/scheduled campaign still using it), then requires the ID typed back, or --confirm <id> in a non-interactive shell. Without that confirmation nothing is deleted. Profiles cannot be deleted through this tool. Two commands can stop mail going to real people, and both require an explicit --yes flag or an interactive confirmation before they run: suppress (reversible — unsuppress undoes it) and unsubscribe (it revokes consent, which unsuppress cannot undo; reversing it takes a deliberate call to Klaviyo's bulk subscribe endpoint asserting fresh consent, which this CLI intentionally does not wrap).
Drop this in your repo's CLAUDE.md so an agent knows the tool exists:
## Klaviyo
Use the `klaviyo` CLI for Klaviyo data and actions (campaigns, segments,
flows, metrics). Auth via KLAVIYO_API_KEY env var. Pass --json before the
command for machine-readable output (e.g. `klaviyo --json list-campaigns`).
Run `klaviyo --help` for the full command list.
Command reference
Run klaviyo COMMAND --help for full options on any command.
Campaigns
Uses API revision 2026-10-15, where a campaign is a container of messages and the subject, sender and template live on each message's content variation. The ID a campaign shows in the Klaviyo UI (and in reporting) is its message ID, so that's the ID every command prints and takes. A container campaign ID also works when the container holds one message. A draft's planned send date has no field at 2026-10-15, so patch-campaign --date, upload-sms and schedule (and the planned date get-campaign shows) use revision 2026-07-15.
| Command | Description |
|---|---|
list-campaigns |
List/filter campaigns by status, channel, and date (beyond name search) |
search-campaigns |
Search campaigns by name |
get-campaign |
Show details for a specific campaign, incl. subject/preview and planned send time; --names resolves audience IDs |
get-creative |
Dump a campaign's creative (subject + text/HTML) via its template |
list-drafts |
List draft email campaigns for a client |
patch-campaign |
Update campaign send time and/or audiences |
patch-message |
Change subject, preview text or sender; re-reads the campaign and fails if the change didn't stick or touched another field |
clone-campaign |
Duplicate a campaign into a new draft and print what it inherited (subject, from address, audiences) plus the new campaign and template IDs |
set-campaign-html |
Replace a campaign's HTML from a file and verify the live creative (--expect/--forbid text checks) |
schedule |
Schedule a campaign for sending |
unschedule |
Take a scheduled campaign back to Draft, keeping its planned send time (never cancels: a cancelled campaign can't be rescheduled or deleted) |
reschedule |
Move a scheduled or draft campaign to a new send time and schedule it; refuses past times and verifies the result |
test-send |
Email a test of a campaign, flow email(s) (--flow ID --email 1,3 or all, numbered as in flow-detail), or template (--template) to up to 5 addresses per batch. --as-profile fills merge tags from a real profile. Arrives as " Preview" from Klaviyo Preview Mailer; Klaviyo allows 100 a day per account |
campaign-performance |
Show campaign revenue and engagement metrics |
metrics |
Show sent campaigns for a client within a date window |
Segments
| Command | Description |
|---|---|
list-audiences |
List all lists and segments for a client |
search-segments |
Find segments by name keyword; shows a one-line definition summary |
get-segment |
Show a segment's definition (conditions, metric IDs resolved) + count |
segment-count |
Get profile count for a single segment (rate limited: 1/s, 15/min) |
segment-sizes |
Show all segments with profile counts |
list-sizes |
Show all lists with profile counts, newest first |
create-segment |
Create a segment from a definition, guarding against duplicates |
update-segment |
Rename a segment and/or replace its definition (before/after preview, --dry-run, verified read-back) |
segment-usage |
Every flow and draft/scheduled campaign that uses a segment or list (routes around campaigns Klaviyo reports as broken) |
Flows
| Command | Description |
|---|---|
flows |
List all flows for a client, with optional sort and name search |
get-flow |
Show a flow's basics in one call; --definition renders the branch tree (splits, messages, delays) |
flow-detail |
Show full flow structure: trigger, filters, emails with subjects, delays, splits |
flow-performance |
Show flow revenue and engagement metrics |
flow-series |
Flow performance over time (daily/weekly/monthly buckets, per flow or per message) |
flow-actions |
List a flow's actions with status and created/updated timestamps (audit view) |
create-flow |
Create a flow (in draft) from a definition, guarding against duplicates |
flow-add-delay |
Insert a delay --before N or --after N (message numbers as flow-detail prints them) with --days/--hours/--minutes |
flow-set-delay |
Change the delay right before message N |
flow-add-email |
Insert an email (created in Draft, sends nothing until switched live); --copy N starts from another email's settings and template, --subject/--preview/--name/--template override |
flow-remove-step |
Remove message N (--message) or the delay before it (--delay-before); asks first unless --yes |
Delays are always live, so adding, changing, or removing one in a live flow affects profiles already in it; those commands refuse a live flow without --live-ok. Klaviyo can't insert conditional/trigger splits or A/B tests, and trigger and profile filters can't be edited through the API at all. Inserts aren't idempotent, so each command reads the flow back instead of retrying.
Profiles
| Command | Description |
|---|---|
get-profile |
Look up a profile by email or ID; --subscriptions adds consent state and suppressions |
segment-members |
List profiles in a segment: email, name, and when they joined |
suppress |
Suppress profiles from email marketing in bulk (requires --yes or confirmation) |
unsuppress |
Remove manual suppressions in bulk (never resubscribes anyone) |
unsubscribe |
Set email consent to UNSUBSCRIBED in bulk — unsuppress can't undo it; --list scopes to one list (requires --yes or confirmation) |
suppression-jobs |
List bulk suppression jobs (suppress + unsuppress) with status and counts |
Events
| Command | Description |
|---|---|
push-event |
Push a custom event to a profile by email (creates the profile if needed) |
events |
List recent events for a metric ID; --since/--until windows, --properties payloads; warns when --limit cuts the window |
export-events |
Bulk-export ALL events for a metric in a window as NDJSON (exhaustive pagination; --out file, --fields, --max-pages guard) |
Metrics
| Command | Description |
|---|---|
account-health |
Show profiles count, lists, and metrics for a client |
list-metrics |
List event metrics with their IDs and integration (ID<->name catalog) |
metric-aggregate |
Bucketed counts for one metric over time (hour/day/week/month, optional group-by) |
forms |
List sign-up forms with live/draft status and dates, newest first (--status, --versions) |
form-performance |
Show pop-up/form views, submits, and submit rates. --form <id> and/or --by-version switch to a per-form report split by version (A/B variations, or before/after each publish); --since/--until for exact windows |
Forms
Uses Forms API revision 2026-10-15 (needs forms:read, plus forms:write to edit). The API only edits draft versions: live versions are echoed back unchanged, versions can't be added to or removed from an existing form, and every edit stays in draft until published. Forms with no display triggers have to be handled in the form editor.
A/B tests: ab-test-form <form_id> --name NAME [--variant A --variant B] creates a new draft form with one A/B variation per --variant, each a copy of the source's live version; edit each with form-replace ... --version <id>. Klaviyo adds one extra non-test version named after the form. The API can't publish a form with a draft A/B test (Klaviyo's rule), so start the test in the form editor. Read results with form-performance --form <id> --by-version. copy-form on a variation makes a plain form from it (e.g. to keep the winner).
Quirks worth knowing before you edit a real form:
- Every save must send the live version back exactly as returned, and Klaviyo often rejects it anyway: editor-built forms carry things its own validator refuses (empty layout rows, URL rules with no URLs, two teasers on one device type), and publishing from the editor does not clean them up. When that happens:
unpublish-form, save the draft (now the only version),publish-form. Same form ID; the form is off the site for a few seconds; if the save fails, republish the untouched draft to restore it. unpublish-formdeletes the live version rather than pausing it. To pause a form, toggle it off in the Klaviyo UI.- Every publish creates a new live version ID (the editor does the same).
- Drafts are cleaned automatically before saving (empty rows, empty URL rules);
copy-formalso drops a second same-device teaser and says so. - Survey/checkbox options have a
labeland avalue; the value is what lands on the profile, so change both. - Delete a form with
klaviyo delete form <id>.
| Command | Description |
|---|---|
form |
Show a form's versions, triggers, and every block's text step by step; --out saves the full definition JSON for editing |
form-replace |
Find/replace any text (copy, labels, URLs) in the draft version(s); --dry-run shows each change; verifies by reading back. Never touches the live version |
update-form |
Save an edited form --out file back as the definition; live versions are restored from the server and version IDs must match |
copy-form |
Create a new draft form from one version of an existing form (--version, default the live one; an A/B variation copies out as a plain form) |
ab-test-form |
Create a new draft A/B test form, one variation per --variant (default A and B), each copied from one version; start it in the form editor |
publish-form |
Publish the draft over the live version; requires the form ID typed back or --confirm <id> |
unpublish-form |
Take a form off the site; same confirmation |
Editing emails
edit-template changes copy, links, or images in an existing email: a template ID, --campaign <id>, or --flow <id> --email N (numbered as in flow-detail). With no edits it lists the email's copy and numbered images.
klaviyo edit-template --campaign <id> --replace "Ends Friday" "Ends Sunday"
klaviyo edit-template --flow TCAqyz --email 2 --image 3 ./new-hero.png --alt 3 "New hero"
klaviyo edit-template <template_id> --replace "utm_campaign=old" "utm_campaign=new" --dry-run
- Works on drag-and-drop and HTML templates.
--replacechanges every match, including button, image, and header links in drag-and-drop emails. Text split by formatting (half bold) won't match; use a shorter piece. If any--replacefinds nothing, nothing is saved. - New images (URL or local JPEG/PNG/GIF up to 5 MB) are imported into the account's image library first, because Klaviyo requires a library asset on every drag-and-drop image. Local files go up as JSON, so this works through JSON-only proxies.
- Klaviyo's API drops crop settings on any drag-and-drop save: a cropped image still shows cropped, but the crop can't be re-adjusted in the editor.
- Templates copied from another Klaviyo account reference that account's images, which Klaviyo rejects on save. Those images are re-imported (hidden) into this account and the save is retried.
- Klaviyo's API can't open some drag-and-drop templates (a header block with a logo in the "links" layout). Edit those in the Klaviyo editor.
- A sent campaign is refused. Scheduled campaigns and live flows are edited with a note that the change goes out on the next send.
convert-template <id> turns an HTML template into a new drag-and-drop one (the original is untouched); convert-template --campaign <id> does it for a draft campaign and puts the drag-and-drop version on the campaign. Paid Klaviyo plans only, one conversion a minute, 1-2 minutes each. Text, links, and images carry over; the hidden preheader div doesn't (use the campaign's preview text). Check the result in the editor before sending.
Billing
| Command | Description |
|---|---|
billing-usage |
This period's usage against plan caps: active profiles, email sends, SMS credits, plus a period-end projection for sends and credits. Flags anything at or over --warn (default 85%) and exits 3 so a scheduled check can alert. Add-on products (Customer Hub, K:Social, reviews) are listed but never flagged: they count every profile against their own small free tier |
Needs the usage:read scope ("Usage: Read Access" on the API key). Keys made before Klaviyo added that scope don't have it, even full-access ones. The API can only read usage; there is no endpoint that changes a plan.
SMS
| Command | Description |
|---|---|
upload-sms |
Create an SMS campaign draft in Klaviyo |
Images
| Command | Description |
|---|---|
upload-image |
Upload a local image to the image library and print its hosted URL (multipart; needs a direct API key) |
Delete
| Command | Description |
|---|---|
delete |
Permanently delete a segment, list, campaign, flow, or form: klaviyo delete <type> <id>. Shows what uses it first, then requires the ID typed back or --confirm <id> |
Raw API
| Command | Description |
|---|---|
api |
Raw API pass-through: klaviyo api <METHOD> <path> |
That's 50 commands total.
Multi-account profiles
For managing more than one Klaviyo account, put credentials in ~/.config/klaviyo-cli/config.toml:
default_profile = "acme"
[profiles.acme]
api_key = "pk_acme_..."
[profiles.other-brand]
api_key = "pk_other_..."
Select a profile with --profile (or -p), or set KLAVIYO_PROFILE:
klaviyo --profile other-brand account-health
KLAVIYO_PROFILE=other-brand klaviyo account-health
Precedence: an explicit --profile (or KLAVIYO_PROFILE) wins first. Otherwise KLAVIYO_API_KEY is used if set. Otherwise the CLI falls back to default_profile in the config file.
Agencies running the CLI across many clients can go further and embed it: re-expose every command under a host CLI with per-client auth resolution, so yourcli campaign-performance acme --days 30 resolves credentials for acme before the call. See the wrap_with_account and build_host_group docstrings in src/klaviyo_cli/embed.py.
License
MIT. See LICENSE.
Built and maintained by BS&Co, a retention marketing agency for eCommerce brands.
Metadata
Release files for klaviyo-cli 0.15.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| klaviyo_cli-0.15.0.tar.gz | 114.2 kB | With provenance | Details for klaviyo_cli-0.15.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| klaviyo_cli-0.15.0-py3-none-any.whl | Python 3 | none | any | Details for klaviyo_cli-0.15.0-py3-none-any.whl |
Total release size: 201.1 kB
Release files / klaviyo_cli-0.15.0.tar.gz
| Download URL | klaviyo_cli-0.15.0.tar.gz |
|---|---|
| Size | 114.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f0a976ad1e4148e3f13958e8b9b0c7846bc71774e97aa3c7938d7e7cc0a2f476
|
|
BLAKE2b-256 checksum How to use checksums |
467e27fcab2f48debe41259c872bd8c0fd495398a19b02a9825a5501f7ce3f80
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 30, 2026.
Transparency logRelease files / klaviyo_cli-0.15.0-py3-none-any.whl
| Download URL | klaviyo_cli-0.15.0-py3-none-any.whl |
|---|---|
| Size | 86.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
07ec5bd340d83d4eaaba4c0d6ff971407295c60332cbd05849adfc6a275a72e3
|
|
BLAKE2b-256 checksum How to use checksums |
cfe9be713905feba4ad1ad134afa2af888ce504cad8ee2834ac87a4127c78c8d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 30, 2026.
Transparency logSecurity for klaviyo-cli 0.15.0
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
Both files in this release have been uploaded with provenance metadata.
Attestation for klaviyo_cli-0.15.0.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 Oct 30, 2026.
Transparency logA 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.