Skip to main content
RapidDev - Software Development Agency
replit-integrationsStandard API Integration

How to Integrate Replit with Joomla

To integrate Replit with Joomla, enable the Joomla Web Services API (introduced in Joomla 4.0), generate an API token in your Joomla user profile, store it in Replit Secrets (lock icon 🔒) as JOOMLA_API_TOKEN, and call the Joomla REST API at /api/index.php/v1/ with a Bearer auth header. You can read and create articles, categories, menus, and users. Use an Autoscale deployment for content sync tools.

What you'll learn

  • How to enable the Joomla Web Services API and generate an API token
  • How to authenticate Replit requests to Joomla with Bearer token headers
  • How to read articles and create new content using the Joomla REST API
  • How to handle Joomla's JSON API response format and pagination
  • Differences between Joomla's API token and OAuth 2.0 for integration scenarios
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate14 min read20 minutesCMSMarch 2026RapidDev Engineering Team
TL;DR

To integrate Replit with Joomla, enable the Joomla Web Services API (introduced in Joomla 4.0), generate an API token in your Joomla user profile, store it in Replit Secrets (lock icon 🔒) as JOOMLA_API_TOKEN, and call the Joomla REST API at /api/index.php/v1/ with a Bearer auth header. You can read and create articles, categories, menus, and users. Use an Autoscale deployment for content sync tools.

Joomla Web Services API Integration from Replit

Joomla 4 introduced a built-in REST API that makes it possible to manage CMS content programmatically without direct database access. The Web Services API exposes core Joomla data: articles (com_content), categories, banners, contacts, fields, menus, modules, users, and more. For Replit developers, this opens up use cases like content migration tools, headless CMS setups where Joomla serves as a backend and a Replit app renders the frontend, bulk content import/export pipelines, and automated publishing workflows.

Joomla's API authentication is simpler than OAuth — each Joomla user account can generate a personal API token in the user's profile settings. The token is a long alphanumeric string included in the Authorization: Bearer header of API requests. This token has the same permissions as the user account that generated it. For integrations that only need to read public articles, no authentication is required at all — the API serves published content without credentials.

Joomla's API follows the JSON:API specification for response structure, which differs slightly from plain REST JSON. Responses wrap data in a data object with type, id, and attributes fields, and relationships are in a separate relationships object. Understanding this structure helps you correctly extract the fields you need from API responses without confusion about the nesting.

Integration method

Standard API Integration

Joomla 4.0 and later includes a built-in Web Services API that exposes articles, categories, fields, menus, modules, and users via REST endpoints. Authentication uses a personal API token generated in your Joomla user profile, sent as a Bearer token in the Authorization header. Your Replit server makes authenticated HTTP calls to the Joomla API at your site's /api/index.php/v1/ path to read content or create and update articles.

Prerequisites

  • A Replit account with a Node.js or Python Repl ready
  • A Joomla 4.0 or later installation with admin access
  • The Joomla Web Services API plugin enabled (System → Manage → Plugins → Web Services - Content)
  • A Joomla API token generated in your user profile settings

Step-by-step guide

1

Enable the Joomla Web Services API and Generate an API Token

The Joomla Web Services API is available in Joomla 4.0+ but must be enabled before use. Log into your Joomla administrator panel and navigate to System → Manage → Plugins. Search for 'Web Services' — you will see plugins like 'Web Services - Content', 'Web Services - Users', 'Web Services - Categories'. Enable each plugin you need by clicking on it and toggling the Status to Enabled. With the plugins enabled, the API is accessible at https://yourdomain.com/api/index.php/v1/ (for most hosting setups). If your Joomla installation uses a subfolder, the API path includes that subfolder: https://yourdomain.com/joomla/api/index.php/v1/. Next, generate your API token. Go to your Joomla user account settings: click your username in the top-right corner of the admin panel → Edit Profile, or navigate to Users → Manage, click your user, then click the 'Joomla API Token' tab. Click 'New Token' and Joomla generates a personal token tied to your account. Copy this token immediately — it will not be shown again after you leave the page. Your Joomla API token inherits your user account's permissions. If your account is a Super Administrator, the token has full API access. For integrations, create a dedicated Joomla user with only the necessary group permissions (e.g., Publisher group for article creation, Manager for category management) and generate the token from that account. Test the API is working by making a simple request: GET https://yourdomain.com/api/index.php/v1/content/articles — this should return published articles without authentication (for a public Joomla site).

Pro tip: If the API returns a 404, check that the Web Services plugins are enabled AND that URL rewriting is working on your Joomla installation. The API requires mod_rewrite (Apache) or the equivalent. Check System → System Information → Directory Permissions to verify the server configuration.

Expected result: Web Services plugins are enabled in Joomla. A personal API token has been generated and copied. GET /api/index.php/v1/content/articles returns JSON with published articles.

2

Store Joomla Credentials in Replit Secrets

Click the lock icon (🔒) in the left Replit sidebar to open the Secrets pane and add the following secrets: JOOMLA_API_TOKEN: the personal API token generated in your Joomla user profile. JOOMLA_BASE_URL: your Joomla site's base URL without a trailing slash (e.g., https://yoursite.com). The API path will be constructed as {JOOMLA_BASE_URL}/api/index.php/v1. These two values are all you need for Bearer token authentication. The token is the most sensitive secret here — it grants the same permissions as your Joomla user account. Store it only in Replit Secrets, never in code files. In your code, build API calls with: Authorization: Bearer {JOOMLA_API_TOKEN} as the header. Joomla also accepts X-Joomla-Token: {token} as an alternative header format for environments where Authorization headers are stripped by proxies. For production integrations where multiple Joomla sites are involved, store credentials per site (JOOMLA_SITE1_TOKEN, JOOMLA_SITE1_URL, etc.) or use a database-backed configuration.

check-joomla-secrets.js
1// check-joomla-secrets.js
2const required = ['JOOMLA_API_TOKEN', 'JOOMLA_BASE_URL'];
3for (const key of required) {
4 if (!process.env[key]) {
5 throw new Error(`Missing secret: ${key}. Set it in Replit Secrets (lock icon 🔒).`);
6 }
7}
8console.log('Joomla config OK. Site:', process.env.JOOMLA_BASE_URL);

Pro tip: If your Joomla site uses a reverse proxy or CDN that strips Authorization headers, use X-Joomla-Token: {token} as the authentication header instead. Both headers are accepted by the Joomla API.

Expected result: JOOMLA_API_TOKEN and JOOMLA_BASE_URL appear in Replit Secrets. The check script prints the site URL without errors.

3

Read and Create Articles via the Joomla API (Node.js)

Install required packages in the Shell tab: npm install axios express. The Joomla REST API uses the JSON:API format — responses wrap data in a data key with attributes, id, and type for each item. When working with list responses, the data key is an array. When creating or updating resources, Joomla expects the same JSON:API format in the request body. Common article fields: title, alias (URL slug, Joomla generates one from title if omitted), introtext (teaser content), fulltext (article body), catid (category ID), state (0=unpublished, 1=published, 2=trashed), language (e.g., * for all), access (1=public), and dates (publish_up, publish_down). For the JSON:API request body when creating articles, wrap your fields in a data.attributes object. Joomla validates the data, creates the article, and returns the full article JSON:API object including the assigned id. The API also supports filtering and pagination on list endpoints. Use query parameters like filter[category_id]=5 to filter articles by category, page[limit]=20 and page[offset]=0 for pagination, and filter[state]=1 to filter by published status. Check the Link header or the links object in the response for pagination navigation URLs. For reading categories, use GET /api/index.php/v1/categories?component=com_content. For users, GET /api/index.php/v1/users (requires Super Administrator token).

joomla.js
1// joomla.js — Joomla Web Services API integration for Node.js on Replit
2const axios = require('axios');
3const express = require('express');
4
5const BASE_URL = process.env.JOOMLA_BASE_URL;
6const TOKEN = process.env.JOOMLA_API_TOKEN;
7const API_URL = `${BASE_URL}/api/index.php/v1`;
8
9const joomlaApi = axios.create({
10 baseURL: API_URL,
11 headers: {
12 'Authorization': `Bearer ${TOKEN}`,
13 'Content-Type': 'application/json'
14 }
15});
16
17const app = express();
18app.use(express.json());
19
20// Get published articles — handles JSON:API response format
21app.get('/api/articles', async (req, res) => {
22 const { catId, limit = 20, offset = 0 } = req.query;
23 const params = {
24 'filter[state]': 1, // Published only
25 'page[limit]': limit,
26 'page[offset]': offset
27 };
28 if (catId) params['filter[category_id]'] = catId;
29
30 try {
31 const response = await joomlaApi.get('/content/articles', { params });
32 // JSON:API: data is an array of { id, type, attributes, relationships }
33 const articles = response.data.data.map(item => ({
34 id: item.id,
35 title: item.attributes.title,
36 alias: item.attributes.alias,
37 introtext: item.attributes.introtext,
38 catid: item.attributes.catid,
39 created: item.attributes.created,
40 state: item.attributes.state
41 }));
42 res.json({ articles, total: response.data.meta?.total || articles.length });
43 } catch (err) {
44 res.status(err.response?.status || 500).json({ error: err.response?.data || err.message });
45 }
46});
47
48// Get single article by ID
49app.get('/api/articles/:id', async (req, res) => {
50 try {
51 const response = await joomlaApi.get(`/content/articles/${req.params.id}`);
52 const item = response.data.data;
53 res.json({ id: item.id, ...item.attributes });
54 } catch (err) {
55 res.status(err.response?.status || 500).json({ error: err.response?.data || err.message });
56 }
57});
58
59// Create a new article
60app.post('/api/articles', async (req, res) => {
61 const { title, introtext, fulltext, catid, state = 1, language = '*' } = req.body;
62
63 // JSON:API format for creating resources
64 const payload = {
65 title,
66 introtext: introtext || '',
67 fulltext: fulltext || '',
68 catid: catid || 1,
69 state,
70 language,
71 access: 1, // Public
72 featured: 0
73 };
74
75 try {
76 const response = await joomlaApi.post('/content/articles', payload);
77 const item = response.data.data;
78 res.json({ success: true, id: item.id, title: item.attributes?.title });
79 } catch (err) {
80 res.status(err.response?.status || 500).json({ error: err.response?.data || err.message });
81 }
82});
83
84// Get all content categories
85app.get('/api/categories', async (req, res) => {
86 try {
87 const response = await joomlaApi.get('/categories', {
88 params: { 'filter[component]': 'com_content' }
89 });
90 const cats = response.data.data.map(c => ({ id: c.id, title: c.attributes.title, alias: c.attributes.alias }));
91 res.json({ categories: cats });
92 } catch (err) {
93 res.status(err.response?.status || 500).json({ error: err.response?.data || err.message });
94 }
95});
96
97app.listen(3000, '0.0.0.0', () => console.log('Joomla API server running on port 3000'));

Pro tip: Joomla returns article content as HTML strings. If you plan to re-render articles in your Replit app, sanitize the HTML before injecting it into pages — Joomla editors can include arbitrary HTML in article bodies.

Expected result: GET /api/articles returns published Joomla articles. POST /api/articles creates a new article and returns its Joomla ID. GET /api/categories returns content categories.

4

Python Integration for Joomla API

For Python Replit projects, install requests and flask: pip install requests flask. The Joomla API integration in Python follows the same pattern: create a requests.Session with the Authorization header set, and call API endpoints. The JSON:API response structure is the same regardless of language — data is an array or object with type, id, and attributes. In Python, access fields with response.json()['data'] and iterate over the list. For single-resource responses, response.json()['data']['attributes'] contains the fields. For bulk content operations (importing hundreds of articles), implement rate limiting in your Python script using time.sleep() between requests to avoid overwhelming your Joomla server. Joomla does not publish explicit rate limits but shared hosting servers can be sensitive to rapid API calls. For error handling, Joomla returns standard HTTP status codes: 200 for success, 201 for created resources, 400 for validation errors (the response body contains error details), 401 for authentication failures, 403 for permission errors, and 404 for resources that do not exist.

joomla_api.py
1# joomla_api.py Joomla Web Services API for Python on Replit
2import os
3import requests
4from flask import Flask, request, jsonify
5
6BASE_URL = os.environ['JOOMLA_BASE_URL']
7TOKEN = os.environ['JOOMLA_API_TOKEN']
8API_URL = f'{BASE_URL}/api/index.php/v1'
9
10# Session with auth header set once
11session = requests.Session()
12session.headers.update({
13 'Authorization': f'Bearer {TOKEN}',
14 'Content-Type': 'application/json'
15})
16
17app = Flask(__name__)
18
19def parse_articles(response_data):
20 """Extract simplified article data from JSON:API format."""
21 return [{
22 'id': item['id'],
23 'title': item['attributes']['title'],
24 'alias': item['attributes']['alias'],
25 'introtext': item['attributes'].get('introtext', ''),
26 'catid': item['attributes'].get('catid'),
27 'state': item['attributes'].get('state'),
28 'created': item['attributes'].get('created')
29 } for item in response_data.get('data', [])]
30
31@app.route('/api/articles')
32def get_articles():
33 params = {
34 'filter[state]': 1,
35 'page[limit]': request.args.get('limit', 20),
36 'page[offset]': request.args.get('offset', 0)
37 }
38 if cat_id := request.args.get('catId'):
39 params['filter[category_id]'] = cat_id
40
41 resp = session.get(f'{API_URL}/content/articles', params=params)
42 if not resp.ok:
43 return jsonify({'error': resp.json()}), resp.status_code
44
45 articles = parse_articles(resp.json())
46 return jsonify({'articles': articles, 'total': len(articles)})
47
48@app.route('/api/articles', methods=['POST'])
49def create_article():
50 data = request.get_json()
51 payload = {
52 'title': data.get('title'),
53 'introtext': data.get('introtext', ''),
54 'fulltext': data.get('fulltext', ''),
55 'catid': data.get('catid', 1),
56 'state': data.get('state', 1),
57 'language': data.get('language', '*'),
58 'access': 1
59 }
60 resp = session.post(f'{API_URL}/content/articles', json=payload)
61 if not resp.ok:
62 return jsonify({'error': resp.json()}), resp.status_code
63
64 item = resp.json().get('data', {})
65 return jsonify({'success': True, 'id': item.get('id'), 'title': item.get('attributes', {}).get('title')})
66
67if __name__ == '__main__':
68 app.run(host='0.0.0.0', port=3000)

Pro tip: For bulk imports, wrap each article creation in a try/except and collect both successes and failures. Log the failed article titles with the error messages so you can investigate and retry them individually rather than losing track of what failed.

Expected result: GET /api/articles returns Joomla articles. POST /api/articles with title, introtext, and catid creates the article in Joomla and returns its ID.

Common use cases

Headless CMS Content Delivery

Use Joomla as a content management backend while serving the frontend from a custom Replit application. Content editors manage articles in the familiar Joomla admin panel, and your Replit app fetches published articles via the API to render them with a custom design or in a non-web format like a mobile app or email newsletter.

Replit Prompt

Build an Express API that fetches published Joomla articles filtered by category, transforms the JSON:API response into a simplified format, and returns article titles, content, and metadata for a headless frontend.

Copy this prompt to try it in Replit

Content Import and Migration

Import content from an external source (CSV, database, another CMS) into Joomla by creating articles via the API. Useful for migrating content during a website redesign, bulk-importing product descriptions, or syncing content from a Google Sheet or Airtable to Joomla.

Replit Prompt

Create a script that reads articles from a CSV file and creates them as Joomla articles via the REST API, mapping CSV columns to Joomla article fields and returning a report of successful imports.

Copy this prompt to try it in Replit

Automated Publishing Pipeline

Automate the scheduling and publishing of Joomla articles from an external editorial workflow. A Replit server monitors a content queue, applies publishing rules (schedule time, category assignment, author), and creates or updates Joomla articles via the API to publish content on schedule.

Replit Prompt

Build a scheduler that reads pending articles from a database, creates them in Joomla with publish_up and publish_down dates set, and logs successful publications with the Joomla article ID.

Copy this prompt to try it in Replit

Troubleshooting

404 Not Found when calling /api/index.php/v1/content/articles

Cause: The Joomla Web Services plugins are not enabled, or the API URL path is wrong for your Joomla installation. The API path includes the subfolder if Joomla is installed in a subdirectory.

Solution: In Joomla admin, go to System → Manage → Plugins and search for 'Web Services'. Verify the 'Web Services - Content' plugin is enabled (Status = Enabled). Also check your JOOMLA_BASE_URL — if Joomla is at yoursite.com/joomla/, the API is at yoursite.com/joomla/api/index.php/v1/.

typescript
1// Test API connectivity with a public endpoint
2const response = await axios.get(`${process.env.JOOMLA_BASE_URL}/api/index.php/v1/content/articles`);
3console.log('Status:', response.status);
4console.log('Response:', JSON.stringify(response.data).substring(0, 200));

401 Unauthorized despite a valid-looking token

Cause: The token was generated correctly but the API plugin for the resource you are accessing is not enabled. Or the token belongs to a Joomla user whose account was disabled or whose Super Admin privileges were revoked.

Solution: Verify the specific Web Services plugin for the resource is enabled (e.g., for /content/articles, Web Services - Content must be enabled). Check that the Joomla user account is still active. Regenerate the token in the user's Joomla API Token tab.

400 Bad Request when creating articles with valid-looking payload

Cause: Required fields are missing or have invalid values. Common issues: catid is 0 or references a non-existent category, title is empty, or the language field value is not in Joomla's languages list.

Solution: Log the full error response body — Joomla returns validation error details in the errors array. Fetch valid category IDs first with GET /api/index.php/v1/categories?filter[component]=com_content and use a valid catid. Use language='*' for all-language articles.

typescript
1// Log full Joomla error response
2catch (err) {
3 console.error('Joomla error:', JSON.stringify(err.response?.data, null, 2));
4}

CORS error when calling the Joomla API from browser-side JavaScript

Cause: Joomla does not send CORS headers by default. Calls to the Joomla API must be made from your Replit server, not from browser-side JavaScript.

Solution: Always make Joomla API calls from your Replit server (server-side code), not from the browser. The browser POSTs to your Replit endpoint, which then calls the Joomla API. This server-side pattern avoids CORS issues and keeps your Joomla token on the server.

Best practices

  • Store JOOMLA_API_TOKEN and JOOMLA_BASE_URL in Replit Secrets (lock icon 🔒) — API tokens have full user account permissions
  • Create a dedicated Joomla user account for API integrations with minimum required permissions rather than using a Super Administrator token
  • Enable only the specific Web Services plugins you need (Content, Categories, Users) rather than enabling all available plugins
  • Always make Joomla API calls from your Replit server, not from client-side JavaScript, to keep tokens server-side and avoid CORS errors
  • Handle JSON:API response format explicitly — Joomla wraps all data in data.attributes, unlike plain REST APIs that return fields at the top level
  • Use filter[state]=1 on article queries to return only published articles and avoid accidentally exposing draft content
  • For bulk imports, add delays between requests to avoid overwhelming your Joomla hosting server with rapid API calls
  • Deploy as Autoscale on Replit for content sync tools since operations are stateless and infrequent

Alternatives

Frequently asked questions

How do I connect Replit to Joomla?

Enable the Joomla Web Services API plugins in System → Manage → Plugins, generate an API token in your user profile settings, store it in Replit Secrets as JOOMLA_API_TOKEN, and make HTTP requests to https://yoursite.com/api/index.php/v1/ with Authorization: Bearer {token} headers from your Replit server code.

Does Replit work with Joomla?

Yes. Joomla 4.0+ includes a built-in REST API accessible over HTTPS from any server, including Replit. Use axios (Node.js) or the requests library (Python) to call Joomla API endpoints with Bearer token authentication. No special plugins are required beyond enabling the built-in Web Services plugins.

What Joomla version is required for the REST API?

The Web Services API was introduced in Joomla 4.0, released in August 2021. If your site runs Joomla 3.x, there is no built-in REST API — you would need a third-party API extension. Joomla 4 and later include the API as a core feature.

How do I store my Joomla API token in Replit?

Click the lock icon (🔒) in the Replit sidebar and add JOOMLA_API_TOKEN with your token value and JOOMLA_BASE_URL with your site URL. Access them in Node.js with process.env.JOOMLA_API_TOKEN or in Python with os.environ['JOOMLA_API_TOKEN']. Set the Authorization: Bearer {token} header on all API requests.

Can I read and write Joomla articles from Replit?

Yes. GET /api/index.php/v1/content/articles retrieves published articles. POST /api/index.php/v1/content/articles with a JSON body creates a new article. PATCH /api/index.php/v1/content/articles/{id} updates an existing article. All write operations require an API token with sufficient permissions (Editor or above in Joomla's user groups).

What deployment type should I use on Replit for Joomla integrations?

Autoscale is suitable for most Joomla integrations since content API calls are stateless. Use Reserved VM only for continuous background sync jobs that must run on a fixed schedule. For content import scripts that run once, you can run them directly in the Replit Shell tab without any deployment.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.