Skip to main content

Export WordPress posts to CSV or MySQL using the REST API

Project description

WordPress Post Exporter

Python tool to export all published posts from a WordPress site using the official REST API (wp-json/wp/v2), including post content and taxonomy/author metadata.

Features

  • Exports all published posts with pagination (per_page=100).
  • Supports csv and sql output.
  • SQL output is MySQL 8 compatible.
  • Includes progress logs in the terminal (with optional --quiet).
  • Tries context=edit first, then automatically falls back to context=view if unauthorized.
  • Preserves the full API payload in raw_post_json for custom fields and future migrations.

Exported Data

Each exported post includes:

  • Core fields: id, status, type, slug, link, date, date_gmt, modified, modified_gmt
  • Title, excerpt, and content in both flavors when available:
    • *_rendered
    • *_raw (typically available with context=edit + permissions)
  • Author:
    • author_id
    • author_name
  • Taxonomies:
    • categories IDs + names
    • tags IDs + names
  • Extra fields:
    • featured_media, comment_status, ping_status, template, format, meta
  • Full raw JSON payload:
    • raw_post_json

Requirements

  • Python 3.10+

Installation

Install from PyPI (recommended)

pip install wp-exporter

This installs the wp-export command globally.

Install from source

# Clone the repository
git clone https://github.com/youruser/wp-exporter.git
cd wp-exporter

# Install in editable mode
pip install -e .

# Or install with dev dependencies
pip install -e ".[dev]"

Local development

  1. (Optional, recommended) create and activate a virtual environment:
python3 -m venv .venv
source .venv/bin/activate
  1. Install dependencies:
pip install -r requirements.txt

Or install the package in editable mode:

pip install -e .

Authentication

Use one of the following:

  1. Bearer token
  • --token
  1. WordPress Application Password (Basic Auth)
  • --username
  • --application-password

Note: public exports also work without credentials in many sites, depending on API exposure rules.

Usage

Using wp-export command (after pip install)

# Public/API-view mode (no auth)
wp-export \
  --base-url https://example.com \
  --format csv \
  --output posts.csv

# Bearer token
wp-export \
  --base-url https://example.com \
  --token YOUR_TOKEN \
  --format csv \
  --output posts.csv

# Username + Application Password
wp-export \
  --base-url https://example.com \
  --username admin \
  --application-password "xxxx xxxx xxxx xxxx" \
  --format sql \
  --output posts.sql

Using python main.py (local development)

# Public/API-view mode (no auth)
python3 main.py \
  --base-url https://example.com \
  --format csv \
  --output posts.csv

# Bearer token
python3 main.py \
  --base-url https://example.com \
  --token YOUR_TOKEN \
  --format csv \
  --output posts.csv

# Username + Application Password
python3 main.py \
  --base-url https://example.com \
  --username admin \
  --application-password "xxxx xxxx xxxx xxxx" \
  --format sql \
  --output posts.sql

CLI Arguments

  • --base-url (required): WordPress site base URL. Example: https://example.com
  • --format: csv or sql (default: csv)
  • --output (required): output file path
  • --token: bearer token
  • --username: WordPress username
  • --application-password: WordPress Application Password
  • --timeout: timeout in seconds per HTTP request (default: 30)
  • --quiet: disable progress logs

Progress Output

By default, the exporter prints progress messages such as:

[INFO] Starting export...
[INFO] Fetching published posts (context=edit)...
[INFO] No permission for context=edit. Falling back to public mode (context=view).
[INFO] [posts] page 1/12 | accumulated: 100
[INFO] [posts] page 2/12 | accumulated: 200
[INFO] Total posts found: 1200
[INFO] Normalized: 1200/1200
[INFO] Writing CSV file to posts.csv...
Export completed. 1200 posts written to: posts.csv

CSV Format Specification

The CSV header columns are generated in this order:

  1. id
  2. date
  3. date_gmt
  4. modified
  5. modified_gmt
  6. slug
  7. status
  8. type
  9. link
  10. title_rendered
  11. title_raw
  12. excerpt_rendered
  13. excerpt_raw
  14. content_rendered
  15. content_raw
  16. author_id
  17. author_name
  18. categories_ids (JSON array string)
  19. categories_names (JSON array string)
  20. tags_ids (JSON array string)
  21. tags_names (JSON array string)
  22. featured_media
  23. comment_status
  24. ping_status
  25. template
  26. format
  27. meta (JSON object string)
  28. raw_post_json (full JSON object string)

Notes:

  • CSV encoding is UTF-8.
  • Content fields may contain long HTML/text.
  • JSON-like columns are serialized as JSON strings.

SQL Format Specification (MySQL 8)

The .sql file includes:

  1. Session setup:
  • SET NAMES utf8mb4;
  • SET time_zone = '+00:00';
  1. Schema reset and creation:
  • DROP TABLE IF EXISTS posts;
  • CREATE TABLE posts (...) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
  1. Batched inserts:
  • INSERT INTO posts (...) VALUES (...), (...), ...;

Column Types

  • id: BIGINT NOT NULL (primary key)
  • author_id, featured_media: BIGINT NULL
  • date, date_gmt, modified, modified_gmt: DATETIME NULL
  • all remaining columns: LONGTEXT NULL

SQL Compatibility Details

  • String escaping is MySQL-safe for:
    • backslash (\\)
    • single quote (\')
    • null byte (\0)
    • line break (\n)
    • carriage return (\r)
    • \x1a (\Z)
  • Inserts are chunked (200 rows per statement) to reduce very large single statements.

Importing SQL into MySQL 8

mysql -u your_user -p your_database < posts.sql

Project Structure

.
├── main.py
├── requirements.txt
└── wp_exporter
    ├── __init__.py
    ├── client.py
    ├── config.py
    ├── exporters.py
    ├── service.py
    └── transformers.py

Troubleshooting

  1. 401 or 403 from API
  • Check credentials/permissions.
  • Confirm REST API is enabled.
  • If context=edit is blocked, the exporter automatically retries with context=view.
  1. Empty *_raw fields
  • Expected when the authenticated user lacks edit-level permissions.
  • In public context, WordPress usually returns only rendered fields.
  1. Timeout/network issues
  • Increase --timeout.
  • Check firewall/WAF/proxy restrictions.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wp_exporter-1.0.0.tar.gz (19.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wp_exporter-1.0.0-py3-none-any.whl (10.5 kB view details)

Uploaded Python 3

File details

Details for the file wp_exporter-1.0.0.tar.gz.

File metadata

  • Download URL: wp_exporter-1.0.0.tar.gz
  • Upload date:
  • Size: 19.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for wp_exporter-1.0.0.tar.gz
Algorithm Hash digest
SHA256 6fa10d622043eafd9da53bc9c81e67f0e355af7097cb6e34c73989f3281143ae
MD5 002559f3929040ed64dad6a9c157a0a8
BLAKE2b-256 035227e34f095668f5e8d11e57ce287b17b88e912c29fb399ed38ce5e1c3123b

See more details on using hashes here.

File details

Details for the file wp_exporter-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: wp_exporter-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 10.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for wp_exporter-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 958fe3dc42433f6a65e7156486da8a834aa75c25385bbd6c9e6da75a35a90779
MD5 a8601c434cf6292939c3cf6682b2fac5
BLAKE2b-256 52dc95f08db05b44008f7a4cf7e5fc93a9b25f964d35869ac697b5907feb8abf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page