REST-native FedEx CLI for small business shippers, with rate-shopping, bulk CSV labels, an address book, and a local SQLite ledger no other tool has.
FedEx retires its SOAP APIs on June 1, 2026 and every existing wrapper goes dark. fedex-pp-cli ships the full FedEx OAuth2 REST surface as a single static Go binary. For small-business shippers it adds the four things SaaS competitors charge for: rate-shopping across every service type, bulk-CSV label printing with adaptive rate limits, a local address book, and a SQLite ledger that powers spend reports, archive search, and accounting export.
Learn more at FedEx.
Authentication
FedEx uses OAuth2 client_credentials. Run fedex auth login once with your sandbox or production Client ID and Client Secret; the CLI mints a 1-hour bearer token, caches it on disk, and refreshes proactively before expiry. Production label printing requires per-project Bar Code Analysis Group (BAG) approval from FedEx — fedex doctor surfaces approval status before you hit it.
Quick Start
# Mint a bearer token from FedEx OAuth2 client_credentials and cache it
fedex auth login --client-id $FEDEX_API_KEY --client-secret $FEDEX_SECRET_KEY --env sandbox
# Verify auth, sandbox/prod routing, and surface any BAG approval gaps before you hit them at ship-time
fedex doctor
# Compare every applicable service type ranked by cost — the most useful one-liner this CLI offers
fedex rate shop --from 90210 --to 10001 --weight 5lb --json --select rates.serviceType,rates.totalNetCharge
# Save a recipient to the local address book so you do not retype it for every shipment
fedex address save --name acme --street '500 Main St' --city Denver --state CO --zip 80202 --country US
# Batch-create labels with adaptive rate limiting; resumable on partial failure
fedex ship bulk --csv orders.csv --service GROUND --resume
Unique Features
These capabilities aren't available in any other tool for this API.
Save money, save time
-
rate shop — Quote rates across every applicable service type in parallel and rank by cost, transit days, or cost-per-day.
When picking the cheapest viable service for a shipment, this collapses 6+ API calls into one ranked answer. The headline cost-saving command for SMB shippers.
fedex rate shop --from 90210 --to 10001 --weight 5lb --json --select rates.serviceType,rates.totalNetCharge,rates.transitTime
-
ship bulk — Create labels for a CSV of orders with adaptive rate limiting, per-row PASS/FAIL accounting, and resume-from-last-success.
The 'I ship 30 packages a day' workflow. Replaces clicking through FedEx Ship Manager one label at a time.
fedex ship bulk --csv orders.csv --service GROUND --output labels/ --resume
-
return create — Generate a Ground Call Tag (return label) against an existing tracking number with one command, optionally emailing it to the recipient.
E-commerce customer-service workflow. Issuing a return label is the most common post-sale interaction.
fedex return create --tracking 794633071234 --reason damaged --email customer@example.com
-
address validate — SHA-256-keyed local cache prevents re-billing the FedEx Address Validation API for repeat lookups.
Direct cost savings — the only feature with a quantifiable per-call $ impact.
fedex address validate --street '1600 Amphitheatre Pkwy' --city 'Mountain View' --state CA --zip 94043 --country US --cache
-
ship etd — Single-command Electronic Trade Documents shipping: uploads commercial invoice, captures docId, and stitches it into the shipment create call.
International shipments require ETD; this collapses an error-prone multi-step into one atomic action.
fedex ship etd --invoice invoice.pdf --orig CN --dest US --recipient-name 'Acme' --weight 2kg --service FEDEX_INTERNATIONAL_PRIORITY
Local state that compounds
-
address save — Save frequently-used recipients to a local address book; reference them by name in ship commands.
Ergonomic parity with paid SaaS competitors. Eliminates retyping addresses for repeat customers.
fedex address save acme --street '500 Main St' --city Denver --state CO --zip 80202 --country US
-
track diff — Show only the tracking events that have appeared since the last poll for each tracking number in the local store.
Replaces tracking-poll dedupe glue agents would otherwise write.
fedex track diff --since 1h --json
-
track watch — Continuously poll a set of tracking numbers and emit new events to stdout, a webhook, or a file as they arrive. Polling alternative to FedEx push notifications.
Most SMBs don't have provisioned push webhooks. Polling daemon is the universal alternative.
fedex track watch --tracking 794633071234 --interval 10m --webhook https://example.com/hook
-
archive — SQLite FTS5 search across every shipment in the local archive — recipient name, address, reference, tracking number, service.
'Did we ship to ACME last week?' — a question SMBs ask constantly.
fedex archive 'warehouse 47' --service GROUND --json
-
spend report — Sum of net charges per service type, lane, or account from the local rate-quote and shipment ledger.
'How much did I spend on FedEx last month?' — the question every SMB owner asks.
fedex spend report --since 30d --by service --json
-
export — Dump shipments + charges + tracking events as CSV or JSON for QuickBooks/Xero reconciliation.
Closes the loop on accounting reconciliation without manual data entry.
fedex export --format csv --since 30d --output fedex-april.csv
-
manifest — Generate a printable PDF/text summary of every shipment created today from the local archive, optionally invoking the Ground EOD close API.
Closes the warehouse-day workflow loop in one command.
fedex manifest --date 2026-05-02 --close --output manifest.md
-
sql — Direct SQLite SELECT queries against shipments, rate_quotes, tracking_events, address_validations, addresses tables.
Escape hatch for arbitrary analytics over the local ledger.
fedex sql "select serviceType, count(*) as n, sum(net_charge) as spend from shipments where created_at > date('now','-30 days') group by serviceType order by spend desc"
Setup smoothness
-
doctor — Verifies OAuth auth, sandbox/prod routing, account-number format, label-format compatibility, and surfaces BAG (Bar Code Analysis Group) approval status.
Avoids the most common 'why won't my labels print' failure mode for first-time users.
fedex doctor
Usage
Run fedex-pp-cli --help for the full command reference and flag list.
Commands
addresses
Address validation
fedex-pp-cli addresses validate - Validate one or more addresses (resolved/standardized form, classification, optional resolved coordinates)
availability
Service availability, special-service options, and transit times
fedex-pp-cli availability services - Get available services for an origin/destination pair
fedex-pp-cli availability special_services - Get available special-service options (alcohol, dangerous goods, signature requirements, etc.)
fedex-pp-cli availability transit_times - Get transit times for an origin/destination pair across services
consolidations
Consolidate multiple shipments into one fulfillment (advanced; for high-volume 3PLs)
fedex-pp-cli consolidations add_shipment - Add shipments to a consolidation
fedex-pp-cli consolidations confirm - Confirm a consolidation (locks rates and triggers shipping)
fedex-pp-cli consolidations confirm_results - Get results of a consolidation confirmation
fedex-pp-cli consolidations create - Create a consolidation
fedex-pp-cli consolidations delete - Delete a consolidation
fedex-pp-cli consolidations delete_shipment - Remove a shipment from a consolidation
fedex-pp-cli consolidations modify - Modify a consolidation
fedex-pp-cli consolidations results - Get consolidation results
fedex-pp-cli consolidations retrieve - Retrieve a consolidation
endofday
Submit and modify Ground end-of-day close (daily manifest)
fedex-pp-cli endofday close - Submit Ground end-of-day close (manifest packages shipped today)
fedex-pp-cli endofday modify - Re-submit or modify a previous end-of-day close
freight_pickups
Schedule, check, and cancel freight (LTL) pickups
fedex-pp-cli freight_pickups availability - Check freight pickup availability
fedex-pp-cli freight_pickups cancel - Cancel a freight pickup
fedex-pp-cli freight_pickups create - Schedule a freight (LTL) pickup
freight_shipments
Create freight (LTL) shipments (for industrial shippers)
fedex-pp-cli freight_shipments create - Create a freight (LTL) shipment
globaltrade
Global trade regulatory details (HS codes, restrictions)
fedex-pp-cli globaltrade regulatory - Get regulatory details (HS codes, harmonized system, country restrictions)
locations
Find FedEx Office, dropoff, and pickup locations
fedex-pp-cli locations find - Find FedEx locations near an address or postal code
openship
Build multi-piece shipments progressively before confirming (advanced; not needed for typical SMB workflows)
fedex-pp-cli openship add_package - Add a package to an open shipment
fedex-pp-cli openship create - Create an open (uncommitted) multi-piece shipment
fedex-pp-cli openship delete - Delete an open shipment
fedex-pp-cli openship delete_package - Delete a package from an open shipment
fedex-pp-cli openship modify - Modify an open shipment
fedex-pp-cli openship modify_package - Modify a package in an open shipment
fedex-pp-cli openship results - Get results of a confirmed open shipment
fedex-pp-cli openship retrieve - Retrieve an open shipment by index
fedex-pp-cli openship retrieve_package - Retrieve a specific package from an open shipment
pickups
Schedule, check availability, and cancel Express/Ground pickups
fedex-pp-cli pickups availability - Check whether pickup is available for a postal code on a date
fedex-pp-cli pickups cancel - Cancel a previously-scheduled Express/Ground pickup
fedex-pp-cli pickups create - Schedule an Express or Ground pickup at a specified address
postal
Postal code validation and country servicing
fedex-pp-cli postal validate - Validate that FedEx services a postal code in a country
rates
Rate quotes for Express, Ground, Home, Ground Economy, and freight
fedex-pp-cli rates quote - Quote rates for a shipment (Express/Ground/Home/Ground Economy)
fedex-pp-cli rates quote_freight - Quote rates for freight (LTL) shipments
shipments
Create, validate, cancel, and retrieve shipments
fedex-pp-cli shipments cancel - Cancel a shipment by tracking number
fedex-pp-cli shipments create - Create a shipment and generate label(s)
fedex-pp-cli shipments results - Retrieve results of an asynchronous shipment job
fedex-pp-cli shipments tag - Create a Ground Call Tag (return label) for an existing shipment
fedex-pp-cli shipments tag_cancel - Cancel a Ground Call Tag
fedex-pp-cli shipments validate - Validate a shipment package without creating a label (catches rejections before billing)
track
Track shipments by tracking number, reference, TCN, or associated shipment; retrieve documents and configure notifications
fedex-pp-cli track associated - Track shipments associated with a master tracking number (multi-piece)
fedex-pp-cli track documents - Retrieve tracking documents (signature proof of delivery, etc.)
fedex-pp-cli track notifications - Configure tracking notifications (email/SMS) for a shipment
fedex-pp-cli track number - Track up to 30 tracking numbers in one call (returns full event timeline per shipment)
fedex-pp-cli track reference - Track by reference number (PO/customer ref/RMA)
fedex-pp-cli track tcn - Track by Transportation Control Number (military/government use)
Cookbook
Patterns combining multiple commands to solve real shipping workflows.
Pick the cheapest service for a single shipment
fedex-pp-cli rate shop --from 98101 --to 10001 --weight 5lb \
--json --select 'rates.service_type,rates.net_amount,rates.transit_days' \
--account "$FEDEX_ACCOUNT_NUMBER"
The selected=1 row is the cheapest viable service. Each quote is also persisted to the local rate_quotes ledger so spend reports can attribute follow-on shipments.
Compare cost vs transit
fedex-pp-cli rate shop --from 98101 --to 10001 --weight 12 --rank-by transit --json
Rank by --rank-by transit to surface fastest service first, then inspect net_amount to see the cost premium.
Bulk-ship from a CSV with safe resume
# First run — adaptive rate limiting, low concurrency, write labels to ./labels/
fedex-pp-cli ship bulk --csv orders.csv --service FEDEX_GROUND \
--output ./labels --concurrency 3 --account "$FEDEX_ACCOUNT_NUMBER"
# Network blip? Re-run with --resume; rows already in the archive are skipped
fedex-pp-cli ship bulk --csv orders.csv --resume --account "$FEDEX_ACCOUNT_NUMBER"
Capture only-new tracking events for a webhook
# One-shot diff — ideal for a cron job or CI step
fedex-pp-cli track diff 794633071234 794633071235 --json
# Long-running watch with webhook fan-out
fedex-pp-cli track watch --tracking 794633071234 \
--interval 5m --webhook https://hooks.example.com/track
Issue a return label and email it to the customer
fedex-pp-cli return create --tracking 794633071234 \
--reason damaged --email customer@example.com \
--account "$FEDEX_ACCOUNT_NUMBER"
Cache address validations to avoid re-billing
fedex-pp-cli address validate \
--street '1600 Amphitheatre Pkwy' --city 'Mountain View' \
--state CA --zip 94043 --country US --cache
The SHA-256-keyed local cache returns the same FedEx-validated payload on repeat calls without hitting the API.
Save a recipient and reuse it
fedex-pp-cli address save acme \
--contact-name "ACME Corp" --street "1 Anvil Way" \
--city Burbank --state CA --zip 91505 --country US
# Look it up later
fedex-pp-cli address list --json --select name,city,state
Search the local shipment archive (FTS5)
# Plain word search across recipient/reference/tracking
fedex-pp-cli archive "acme corp"
# Filter by service and recency, render as table
fedex-pp-cli archive --service FEDEX_GROUND --since 720h --limit 25
Run ad-hoc SQL against the local store
# Top spend lanes in the last 30 days
fedex-pp-cli sql "
SELECT origin_postal, dest_postal, SUM(net_amount) AS spend
FROM rate_quotes
WHERE created_at >= datetime('now','-30 days')
GROUP BY 1,2 ORDER BY 3 DESC LIMIT 10"
Build a daily manifest, optionally close end-of-day
# Markdown manifest of every shipment created today
fedex-pp-cli manifest --output today.md
# Same thing, then call FedEx end-of-day to formally close it
fedex-pp-cli manifest --close
Compose a spend report grouped by service or lane
fedex-pp-cli spend report --by service --since 720h
fedex-pp-cli spend report --by lane --since 168h --json
Pre-flight check before a bulk run
# Verify auth, sandbox/prod routing, and BAG approval status
fedex-pp-cli doctor
# Verify a specific call without sending it
fedex-pp-cli ship bulk --csv orders.csv --dry-run
Output Formats
# Human-readable table (default in terminal, JSON when piped)
fedex-pp-cli addresses
# JSON for scripting and agents
fedex-pp-cli addresses --json
# Filter to specific fields
fedex-pp-cli addresses --json --select id,name,status
# Dry run — show the request without sending
fedex-pp-cli addresses --dry-run
# Agent mode — JSON + compact + no prompts in one flag
fedex-pp-cli addresses --agent
Agent Usage
This CLI is designed for AI agent consumption:
- Non-interactive - never prompts, every input is a flag
- Pipeable -
--json output to stdout, errors to stderr
- Filterable -
--select id,name returns only fields you need
- Previewable -
--dry-run shows the request without sending
- Retryable - creates return "already exists" on retry, deletes return "already deleted"
- Confirmable -
--yes for explicit confirmation of destructive actions
- Piped input - write commands can accept structured input when their help lists
--stdin
- Agent-safe by default - no colors or formatting unless
--human-friendly is set
Exit codes: 0 success, 2 usage error, 3 not found, 4 auth error, 5 API error, 7 rate limited, 10 config error.
Health Check
fedex-pp-cli doctor
Verifies configuration, credentials, and connectivity to the API.
Configuration
Config file: ~/.config/fedex-pp-cli/config.toml
Environment variables:
FEDEX_API_KEY
FEDEX_SECRET_KEY
Troubleshooting
Authentication errors (exit code 4)
- Run
fedex-pp-cli doctor to check credentials
- Verify the environment variable is set:
echo $FEDEX_API_KEY
Not found errors (exit code 3)
- Check the resource ID is correct
- Run the
list command to see available items
API-specific
- HTTP 403 with no body, then no responses for 10 minutes — You hit the per-IP throttle (1 req/s sustained for 2 min). Use
fedex ship bulk --concurrency 1 or wait 10 minutes; cliutil.AdaptiveLimiter prevents this when used
- 401 NOT.AUTHORIZED.ERROR on every call — Token expired or credentials wrong. Run
fedex auth login again with valid Client ID + Client Secret
- PDF labels won't print on thermal printer — Pass --label-format ZPLII or EPL2 to ship create; thermal printers can't render PDF directly
- Production ship returns BAG-not-approved error — Production label printing requires per-project FedEx Bar Code Analysis Group approval. Run
fedex doctor for status; submit your BAG application via the FedEx developer portal
- track diff returns nothing despite known new events — Run
fedex track number <num> first to seed the local store; diff compares against what's already cached
Sources & Inspiration
This CLI was built by studying these projects and resources:
Generated by CLI Printing Press