To integrate Replit with WordPress, enable Application Passwords in WordPress (Settings > Users > Application Passwords), store the credentials in Replit Secrets, and call the WordPress REST API from your Replit server-side code. You can read and create posts, pages, and media using standard HTTP requests β no plugin required for read access, and Application Passwords handle write authentication securely.
Manage WordPress Content Programmatically with Replit
WordPress's built-in REST API turns your CMS into a headless content platform β every post, page, category, tag, and media file is accessible via standard HTTP requests. This makes it possible to build entirely custom frontends that consume WordPress content, automate content publishing workflows, sync WordPress with external data sources, and integrate your blog or website with any tool that can make HTTP requests.
From Replit, you can build content automation workflows that would otherwise require manual WordPress admin work: automatically creating posts from RSS feeds or social media, publishing product updates when your database changes, bulk-editing meta descriptions for SEO, or generating scheduled content from a data source. Replit's persistent server environment and Autoscale deployment make it straightforward to run these workflows reliably.
Authentication uses WordPress Application Passwords β a built-in feature (WordPress 5.6+) that generates per-application credentials without exposing your main admin password. Each application (your Replit integration) gets its own revokable password, giving you fine-grained access control. The REST API also supports JWT authentication and OAuth via plugins, but Application Passwords require no extra plugins and work out of the box.
This tutorial covers both read workflows (fetching content for headless frontends or analytics) and write workflows (publishing posts, uploading media), using both Python and Node.js implementations.
Integration method
The WordPress REST API is built into WordPress core (version 4.7+) and does not require any plugins for basic read access. Write operations (creating/updating posts) require authentication via Application Passwords, which are stored in Replit Secrets and sent as Basic Auth headers. Your Replit server calls the API to manage content programmatically.
Prerequisites
- A self-hosted WordPress site (WordPress.com business plan or above also supports the REST API) running WordPress 5.6 or later
- Administrator or Editor access to the WordPress admin to enable Application Passwords
- A Replit account β free tier works for scripts; Replit Core recommended for production services
- Basic Python (requests) or Node.js (fetch) knowledge
- The base URL of your WordPress site (e.g., https://yoursite.com)
Step-by-step guide
Enable Application Passwords and Generate Credentials
Enable Application Passwords and Generate Credentials
Application Passwords are built into WordPress 5.6 and later. They allow you to create separate per-application credentials without sharing your main admin password. Each Application Password can be revoked independently, so you can disable Replit's access without affecting other integrations. To generate an Application Password, log in to your WordPress admin dashboard. In the left sidebar, go to Users > Profile (or Users > All Users, then click your username). Scroll down to the Application Passwords section near the bottom of the profile page. If you do not see this section, check that your WordPress version is 5.6+ and that Application Passwords are not disabled by a security plugin. In the 'New Application Password Name' field, enter a descriptive name like 'Replit Integration' so you know what this password is for. Click 'Add New Application Password'. WordPress generates a password in the format XXXX XXXX XXXX XXXX XXXX XXXX (with spaces). Copy this entire value immediately β WordPress will not show it again. For authentication, you will use your WordPress username (the login username, not your display name) combined with this Application Password. The credentials are sent as HTTP Basic Authentication: base64-encoded 'username:application_password' in the Authorization header. Remove the spaces from the Application Password before encoding.
Pro tip: Note exactly which WordPress username you are using with the Application Password β it should be your admin username (the login name, visible at the top of your WordPress profile page under 'Username'), not your display name or email address.
Expected result: You have a WordPress Application Password for 'Replit Integration' copied, along with your WordPress admin username and your site's URL.
Store WordPress Credentials in Replit Secrets
Store WordPress Credentials in Replit Secrets
Open your Replit project and click the lock icon (π) in the left sidebar to open the Secrets panel. Create three secrets: WP_SITE_URL (your WordPress site URL, e.g., https://yoursite.com β no trailing slash), WP_USERNAME (your WordPress admin username), and WP_APP_PASSWORD (the Application Password you just generated, including any spaces β they will be removed in code). In Python, access them via os.environ['WP_SITE_URL']. In Node.js, use process.env.WP_SITE_URL. After adding secrets, restart the Repl if it is already running. The WordPress REST API base URL is your site URL followed by /wp-json/wp/v2/. For example, to list posts: GET https://yoursite.com/wp-json/wp/v2/posts. Authentication is HTTP Basic Auth β the Python requests library handles this with the auth=(username, password) parameter, and in Node.js you encode credentials as base64. Verify your credentials by making a read request to the /wp-json/wp/v2/users/me endpoint, which returns your user profile if authentication succeeds. A 200 response with your username confirms credentials are correct.
1import os2import requests34WP_URL = os.environ['WP_SITE_URL'].rstrip('/')5WP_USER = os.environ['WP_USERNAME']6WP_PASS = os.environ['WP_APP_PASSWORD'].replace(' ', '') # remove spaces from app password78api_base = f'{WP_URL}/wp-json/wp/v2'9auth = (WP_USER, WP_PASS)1011# Test: verify credentials by fetching current user profile12resp = requests.get(f'{api_base}/users/me', auth=auth)1314if resp.status_code == 200:15 user = resp.json()16 print(f'Authenticated as: {user.get("name")} ({user.get("slug")})')17 print(f'Role: {list(user.get("roles", []))[0] if user.get("roles") else "unknown"}')18elif resp.status_code == 401:19 print('Authentication failed β check WP_USERNAME and WP_APP_PASSWORD in Secrets')20else:21 print(f'Error: {resp.status_code} β {resp.text[:200]}')Pro tip: Remove spaces from the Application Password before sending it. WordPress Application Passwords are displayed with spaces for readability (like a credit card number), but the actual credential has no spaces. Use .replace(' ', '') to strip them.
Expected result: The test script prints 'Authenticated as: [your name]' confirming that the Application Password credentials are working correctly.
Read Posts, Pages, and Media
Read Posts, Pages, and Media
WordPress REST API endpoints follow consistent URL patterns. For posts: GET /wp-json/wp/v2/posts. For pages: GET /wp-json/wp/v2/pages. For media: GET /wp-json/wp/v2/media. Each endpoint supports a comprehensive set of query parameters for filtering, searching, pagination, and field selection. Read endpoints work without authentication for published content (any public post or page is readable without credentials). Authentication is only required to read draft posts or to perform write operations. This means your Replit server can fetch public WordPress content without needing to expose any credentials to the frontend. The script below demonstrates common read operations: listing recent posts with filtering, searching by keyword, and fetching a specific post by slug. The _fields parameter limits the API response to only the fields you need, which speeds up requests and reduces data transfer β especially useful when fetching many posts.
1import os2import requests34WP_URL = os.environ['WP_SITE_URL'].rstrip('/')5WP_USER = os.environ['WP_USERNAME']6WP_PASS = os.environ['WP_APP_PASSWORD'].replace(' ', '')78api_base = f'{WP_URL}/wp-json/wp/v2'9auth = (WP_USER, WP_PASS) # needed for drafts; public posts work without auth1011def get_posts(per_page=10, status='publish', search=None):12 """Fetch published posts with optional search."""13 params = {14 'per_page': per_page,15 'status': status,16 '_fields': 'id,title,slug,status,date,excerpt,categories,tags'17 }18 if search:19 params['search'] = search20 resp = requests.get(f'{api_base}/posts', auth=auth, params=params)21 resp.raise_for_status()22 return resp.json()2324def get_post_by_slug(slug):25 """Fetch a single post by its slug."""26 resp = requests.get(f'{api_base}/posts', auth=auth, params={'slug': slug})27 resp.raise_for_status()28 posts = resp.json()29 return posts[0] if posts else None3031def get_media(per_page=20):32 """Fetch media library items."""33 resp = requests.get(f'{api_base}/media', auth=auth, params={'per_page': per_page})34 resp.raise_for_status()35 return resp.json()3637# Example usage38print('Recent posts:')39posts = get_posts(per_page=5)40for post in posts:41 title = post['title']['rendered']42 print(f" [{post['id']}] {title} ({post['date'][:10]})")4344print(f'\nTotal fetched: {len(posts)}')Pro tip: Use the _fields parameter to request only the data fields you need. WordPress REST API responses include many fields (rendered HTML content, GUID, links, etc.) that you may not need, and fetching only required fields speeds up requests noticeably for lists of posts.
Expected result: The script prints a list of your 5 most recent published WordPress posts with their IDs, titles, and publication dates.
Create and Publish Posts from Replit
Create and Publish Posts from Replit
Writing to WordPress requires authentication via Application Passwords. Creating a post is a POST request to /wp-json/wp/v2/posts with a JSON body containing the post title, content, status (draft or publish), categories, and other fields. Updating an existing post uses a POST or PUT request to /wp-json/wp/v2/posts/{id}. WordPress content can be sent as plain text or HTML. For formatted posts (with headings, lists, images), send HTML in the content field. Gutenberg block format is not required β classic HTML works correctly via the REST API and renders properly in the WordPress editor. The code below creates a new WordPress post, publishes it immediately, and assigns categories and tags. It also shows how to upload media (images) to the WordPress media library via the /media endpoint β an important step for creating featured images for posts.
1import os2import requests34WP_URL = os.environ['WP_SITE_URL'].rstrip('/')5WP_USER = os.environ['WP_USERNAME']6WP_PASS = os.environ['WP_APP_PASSWORD'].replace(' ', '')78api_base = f'{WP_URL}/wp-json/wp/v2'9auth = (WP_USER, WP_PASS)1011def create_post(title, content, status='draft', categories=None, tags=None, excerpt=''):12 """Create a new WordPress post."""13 data = {14 'title': title,15 'content': content,16 'status': status, # 'draft' or 'publish'17 'excerpt': excerpt,18 'format': 'standard'19 }20 if categories:21 data['categories'] = categories # list of category IDs22 if tags:23 data['tags'] = tags # list of tag IDs2425 resp = requests.post(f'{api_base}/posts', auth=auth, json=data)26 resp.raise_for_status()27 return resp.json()2829def update_post(post_id, updates):30 """Update fields on an existing post."""31 resp = requests.post(f'{api_base}/posts/{post_id}', auth=auth, json=updates)32 resp.raise_for_status()33 return resp.json()3435def upload_image(image_path, filename, alt_text=''):36 """Upload an image to WordPress media library."""37 with open(image_path, 'rb') as f:38 resp = requests.post(39 f'{api_base}/media',40 auth=auth,41 headers={'Content-Disposition': f'attachment; filename={filename}'},42 files={'file': (filename, f, 'image/jpeg')},43 data={'alt_text': alt_text}44 )45 resp.raise_for_status()46 return resp.json()4748# Create a draft post49new_post = create_post(50 title='Automated Post from Replit',51 content='<h2>Introduction</h2><p>This post was created via the WordPress REST API from a Replit backend.</p>',52 status='draft',53 excerpt='A demonstration of WordPress REST API integration with Replit.'54)55print(f'Created post ID {new_post["id"]}: {new_post["link"]}')5657# Publish it immediately58published = update_post(new_post['id'], {'status': 'publish'})59print(f'Post published: {published["status"]}')Pro tip: Always create posts as 'draft' first and verify them in the WordPress admin before setting status to 'publish'. This gives you a chance to review automated content before it goes live.
Expected result: The script creates a new WordPress post as a draft and then publishes it, printing the post ID and live URL. The post is visible in your WordPress dashboard and on the live site.
Common use cases
Automated Content Publisher
Your Replit server monitors an RSS feed, a Google Sheet, or a database, and automatically creates WordPress posts when new content is detected. Each post is formatted with the correct category, tags, and featured image, then either published immediately or saved as a draft for editorial review. This eliminates manual copy-paste publishing workflows.
Build a Python script that reads from a Google Sheet with blog post titles and content, then calls the WordPress REST API to create a new draft post for each row, assigning the correct category and tags based on a topic column.
Copy this prompt to try it in Replit
Headless WordPress Content API
Your Replit server acts as a middleware API between WordPress and a custom React or Next.js frontend. It fetches posts from WordPress, normalizes the response format (removing unnecessary fields, transforming the ACF custom fields), adds authentication or caching, and returns clean JSON to the frontend. This simplifies the frontend code and adds a security layer.
Create a Node.js Express server that fetches WordPress posts via the REST API, normalizes the response to only include title, slug, content, featured_image_url, and published_date, then serves the clean JSON to a React frontend.
Copy this prompt to try it in Replit
SEO Bulk Update Tool
You have 200 WordPress posts with missing or poorly written meta descriptions. Your Replit script fetches all posts via the API, filters for those with empty Yoast SEO meta descriptions, generates improved descriptions using an AI API, and updates each post via the WordPress REST API. What would take days of manual work is done in minutes.
Write a Python script that fetches all WordPress posts with missing meta descriptions via the REST API, sends the post title and excerpt to the OpenAI API to generate an optimized meta description, then updates each post via a PUT request to the WordPress API.
Copy this prompt to try it in Replit
Troubleshooting
401 Unauthorized when authenticating with Application Password
Cause: The Application Password contains spaces that were not removed before encoding, the username is the display name instead of the login username, or the Application Password was not generated for the correct user.
Solution: Remove all spaces from the Application Password: password.replace(' ', ''). Verify you are using the WordPress login username (shown in your profile under 'Username'), not the display name or email. Regenerate the Application Password if in doubt and update the Replit Secret.
1# Strip spaces from Application Password2WP_PASS = os.environ['WP_APP_PASSWORD'].replace(' ', '')3print(f'Password length after strip: {len(WP_PASS)}') # should be 24 chars404 Not Found when accessing /wp-json/wp/v2/posts
Cause: The WordPress REST API is disabled, your WordPress permalinks are set to 'Plain' (/%postname%/ style required), or your site URL in Replit Secrets has a trailing slash or typo.
Solution: In WordPress admin, go to Settings > Permalinks and ensure you are NOT using the 'Plain' permalink structure β any other option (Post name is recommended) enables the REST API routing. Remove trailing slashes from WP_SITE_URL in Replit Secrets. Test the URL manually in a browser: yoursite.com/wp-json/wp/v2/posts should return JSON.
REST API requests work in browser but fail from Replit with connection errors
Cause: Your WordPress site may have a firewall rule, Cloudflare security setting, or security plugin (Wordfence, iThemes Security) that blocks requests from cloud server IP ranges like Replit's.
Solution: Check your WordPress security plugin settings for REST API restrictions or IP allowlisting. In Wordfence, go to Firewall > All Firewall Options and check if REST API access is blocked for non-logged-in users or specific IP ranges. Add a rule to allow Replit's IP range or whitelist the specific endpoints you need.
Post created via API shows unstyled HTML instead of formatted content
Cause: The content was sent as plain text but WordPress interpreted it literally, or the content includes unescaped special characters that broke the HTML.
Solution: Send content as properly formatted HTML in the content field. WordPress REST API accepts HTML directly β use standard tags like <p>, <h2>, <ul>, <li>, <strong>. If the content contains user-supplied text, sanitize it to prevent XSS and escape special characters before including in HTML.
1import html23# Escape user content before embedding in HTML4safe_content = html.escape(user_text)5html_content = f'<h2>Introduction</h2><p>{safe_content}</p>'Best practices
- Store WordPress Application Passwords in Replit Secrets (lock icon π) β never hardcode credentials in source code.
- Use WordPress Application Passwords (not your main admin password) for API access β each application gets its own revokable credential.
- Create posts as draft status first, then publish after review β this prevents accidental publishing of incomplete or incorrectly formatted automated content.
- Strip spaces from Application Passwords before using them in authentication: password.replace(' ', ''). WordPress displays them with spaces for readability but the actual value has no spaces.
- Use the _fields parameter to request only the fields you need from list endpoints β this significantly speeds up requests when fetching many posts.
- Implement pagination for fetching large numbers of posts: use the page and per_page parameters, and check the X-WP-TotalPages response header to know when you have fetched all pages.
- Test your WordPress API integration against a staging or development site before running automated publishing scripts against your production site.
- Handle WordPress API rate limits gracefully by adding delays between bulk operations and implementing retry logic with exponential backoff.
Alternatives
Ghost has a more developer-friendly Content API and Admin API with better documentation, making it easier to integrate with Replit if you are starting a new headless CMS project rather than migrating from WordPress.
If your WordPress site runs WooCommerce, the WooCommerce REST API provides dedicated endpoints for products, orders, and customers that are better suited to e-commerce automation than the base WordPress API.
If your primary goal is managing SEO metadata on WordPress posts, integrating with Yoast SEO's REST API fields gives you direct access to meta titles, descriptions, and schema data alongside the standard WordPress post fields.
Frequently asked questions
Does WordPress have a REST API I can call from Replit?
Yes. WordPress has a built-in REST API available at yoursite.com/wp-json/wp/v2/. Reading public posts and pages requires no authentication. Writing (creating/updating posts) requires authentication via WordPress Application Passwords, a built-in feature available in WordPress 5.6+. No plugins are required for the core REST API.
How do I store my WordPress credentials securely in Replit?
Click the lock icon (π) in the Replit sidebar to open Secrets, then add WP_SITE_URL, WP_USERNAME, and WP_APP_PASSWORD as separate secrets. Generate the Application Password in WordPress Admin > Users > Profile > Application Passwords. Access secrets in code via os.environ['WP_SITE_URL'] (Python) or process.env.WP_SITE_URL (Node.js).
Do I need a WordPress plugin for the REST API?
No. The WordPress REST API is built into WordPress core and does not require any plugins for basic post, page, and media operations. However, some third-party data (like Advanced Custom Fields values, WooCommerce products, or Yoast SEO metadata) requires their respective plugins to be installed and activated for that data to appear in the API response.
Can Replit read WordPress posts without authentication?
Yes, for publicly published content. GET requests to /wp-json/wp/v2/posts, /pages, /media, and other public endpoints work without any credentials. Authentication is only required to read draft or private posts, and for all write operations (creating, updating, deleting posts). This makes it safe to call public endpoints from your frontend without exposing credentials.
Can I use Replit to automate bulk WordPress post updates?
Yes. A common use case is fetching all posts with the WordPress API, running transformations (improving meta descriptions, fixing formatting, adding tags), and updating each post via PUT requests. Add delays between requests (0.5-1 second per request) to avoid overloading your WordPress server during bulk operations, especially on shared hosting.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation