PEWPMT is a free and open source lightweight python console app designed to simplify the migration of WordPress posts from an old website to a new one. It leverages the WordPress REST API to download posts and to upload these posts to another WordPress site.
Migrating a large amount of content can be impossibly challenging using wordpress plugins, especially when you refuse to pay for the most basic functionality or need the tiniest bit of customization. This tool was built to:
- Download posts via the WordPress REST API.
- Save posts in a JSON file.
- Upload posts (with featured images) to a new WordPress site.
It is ideal for developers or website administrators who need a free solution for content migration.
- Automated Retrieval: Fetch posts page by page from the source site using the WordPress REST API.
- JSON Output: Save all fetched posts in the
wordpress_posts.json
file for easy access and processing. - Rate Limit Handling: Both scripts include delays (2 seconds in downloader and 1 second in uploader) to help avoid triggering API rate limits.
- Image Handling: Uploader script processes featured images – downloading the image data from the old site and re-uploading it to the target.
- Clone or download the repository to your local machine
- Open a terminal and cd into the project folder
- Make sure Python is installed by running
python -V
- Install requirements by running
pip install -r requirements.txt
- Replace the placeholder urls in
downloader.py
anduploader.py
(see Configuration) - Fill in your credentials in
uploader.py
, it's recommended that you generate a wordpress application password for this - Open a terminal again, cd into the project folder and run
python downloader.py
- Check the content of your generated .json file and upload the posts by running
python uploader.py
⚠️ WARNING: Don't upload everything on your first try! Test it with a few posts first.
Open downloader.py and update the base_url variable with your source WordPress site's REST API endpoint. For example:
base_url = "https://<mysourcewordpresssite.com>/wp-json/wp/v2/posts"
If you are using a free WordPress.com site, you might need to use an alternative URL. Uncomment and adjust as needed:
base_url = "https://public-api.wordpress.com/wp/v2/sites/<mysourcewordpresssite.wordpress.com>/posts"
Set the per_page variable to control how many posts to fetch per API call.
The uploader reads from wordpress_posts.json. Ensure this file exists (generated by running downloader.py successfully).
Open uploader.py and update the variables with your source and target WordPress site's REST API endpoint. For example:
old_media_base_url = "https://<mysourcewordpresssite.com>/wp-json/wp/v2/media/"
target_base_url = "https://<targetsite.com>/wp-json/wp/v2/posts"
new_media_base_url = "https://<targetsite.com>/wp-json/wp/v2/media"
Update the credentials in uploader.py with your target WordPress username and app password:
target_username = '<username>' # Replace with your actual username.
target_password = '<password>' # Replace with your WordPress app password.
The uploader script includes a function upload_image() which downloads the image data from the old site, fetches the actual image URL, and uploads it to the target site. This function also adds a delay to avoid rate limits. Shut this off to speed up the process or lose your mind trying to understand why we have to ask the API for the image url twice.
Add a break command in the for loop in uploader.py, for example:
for idx, post in enumerate(all_posts, start=1):
if idx > 4:
break
To ensure you only get the info you need you will have to add or remove categories of data set by the new_post_data
variable in the uploader.py
file.
The default option only copies the title, the text content and the image of the original post. To see what other info is available, consult the relevant wordpress api docs.
new_post_data = {
"title": post.get("title", {}).get("rendered", "Untitled"), # Post title
"content": post.get("content", {}).get("rendered", ""), # Post text content
"featured_media": image_id if image_id else None, # Post image
# ...
}
Contributions are welcome! If you have ideas to improve the migration process or find any issues, please submit a pull request or open an issue describing the problem.
If you have any questions, suggestions, or feedback, feel free to message me on bluesky @devpersi.bsky.social
Happy migrating!