Overview
The Indexceptional REST API lives under the index/api/v1 namespace and currently exposes authenticated endpoints for checking credits, submitting indexing orders, listing orders, checking order status, and checking indexed status.
This page reflects the current code in class-api.php, class-check-indexed.php, class-rate-limiter.php, and CreditNex\App\Credits\Pricing.
Authentication
Authentication uses Basic Auth with a WordPress Application Password. In class-api.php, the determine_current_user filter accepts credentials from PHP_AUTH_USER and PHP_AUTH_PW, or from a raw Authorization: Basic ... header that is base64-decoded and passed to wp_authenticate().
- Log in to your Indexceptional account.
- Open the Account page and go to the API Access tab.
- Create a WordPress Application Password for your integration.
- Use your WordPress username as the Basic Auth username.
- Use the generated application password exactly as issued as the Basic Auth password.
Authorization: Basic {base64(username:application-password)}
Content-Type: application/json
Accept: application/jsonBase URL
https://www.indexceptional.com/wp-json/index/api/v1Namespace: index/api/v1
Example with query parameters: https://www.indexceptional.com/wp-json/index/api/v1/orders?per_page=20&page=1&status=completed
Endpoints
Check Balance
Returns the authenticated user's current credit balance.
GET https://www.indexceptional.com/wp-json/index/api/v1/check-balanceResponse shape: { success: true, data: { credits: int } }
creditsis an integer balance.-1means the account has unlimited credits.
Example Response
{
"success": true,
"data": {
"credits": 125
}
}Submit Order
Creates a new indexing order for the authenticated user.
POST https://www.indexceptional.com/wp-json/index/api/v1/orderRequest Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| urls | string[] | Yes | Array of URLs to submit. |
| product_id | integer | No | Optional WooCommerce product ID. Only products with slug index or drip-index are accepted. |
| validated | boolean | No | Optional client flag indicating the URLs were HTTP-validated before submission. Defaults to false. |
Pricing
- Slug
indexcosts5credits per URL. - Slug
drip-indexcosts1credit per URL. - If
product_idis omitted, the endpoint defaults to slugdrip-index. Pricing::ALLOWED_SLUGSonly allowsdrip-indexandindex.- The
indexceptional_credit_costsfilter can raise prices above the defaults, but it cannot lower them.
Order Submission Limits
- Maximum
500URLs per order. - Minimum
20seconds between order submissions. - Maximum
10orders per day.
Request Body Example
{
"urls": [
"https://example.com/page1",
"https://example.com/page2"
],
"product_id": 1234,
"validated": true
}Success Response Fields
order_id: WooCommerce order ID.status: Current order status.total_urls: Number of submitted URLs.credits_charged: Total credits debited for this order.credits_remaining: Updated credit balance after payment. Unlimited accounts return-1.view_order_url: WooCommerce order view URL.urls: Array of per-URL objects withurl,date_created,status, andbot_ip.
Example Response
{
"success": true,
"data": {
"order_id": 12345,
"status": "api-ordered",
"total_urls": 2,
"credits_charged": 2,
"credits_remaining": 123,
"view_order_url": "https://www.indexceptional.com/my-account/view-order/12345/",
"urls": [
{
"url": "https://example.com/page1",
"date_created": "2026-05-22",
"status": "queued",
"bot_ip": "-"
},
{
"url": "https://example.com/page2",
"date_created": "2026-05-22",
"status": "queued",
"bot_ip": "-"
}
]
}
}Common Error Codes
missing_urls: The request did not include a valid URL array.too_many_urls: More than500URLs were submitted.rate_limit_exceeded: Another order was submitted less than20seconds ago.daily_limit_exceeded: The user already placed10orders today.product_not_found: No allowed indexing product could be resolved.product_not_priceable: The chosen product is not a recognized credit-purchase indexing product.insufficient_credits: The account balance is lower than the required credit charge.payment_failed: CreditNex payment processing did not succeed.
Get Orders
Returns the authenticated user's orders as a paginated list of WooCommerce order IDs.
GET https://www.indexceptional.com/wp-json/index/api/v1/orders?per_page=10&page=1&status=anyQuery Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| per_page | integer | No | 10 | Orders per page. Sanitized with absint. |
| page | integer | No | 1 | Page number. Sanitized with absint. |
| status | string | No | any | Optional order status filter. |
Allowed Status Values
anyapi-orderedmanual-actiongbot-visitedchecking-indexcompleted
Example Response
{
"success": true,
"data": {
"orders": [12345, 12346, 12347],
"total": 23,
"per_page": 10,
"current_page": 1,
"total_pages": 3
}
}If status is invalid, WordPress REST argument validation rejects the request before the callback runs.
Check Order Status
Returns the current status of a specific order owned by the authenticated user.
GET https://www.indexceptional.com/wp-json/index/api/v1/order/status/{order_id}URL Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| order_id | integer | Yes | WooCommerce order ID. |
Success Response Fields
order_idstatusdate_createdin ISO 8601 format from$order->get_date_created()->format('c').total_urlsvalidated_urlsas a boolean derived from item meta.urlswith per-URL fields:url,indexable,date_created,status,date_indexed,serp, andbot_ip.
Example Response
{
"success": true,
"data": {
"order_id": 12345,
"status": "api-ordered",
"date_created": "2026-05-22T12:00:00+00:00",
"total_urls": 2,
"validated_urls": true,
"urls": [
{
"url": "https://example.com/page1",
"indexable": "200",
"date_created": "2026-05-22",
"status": "queued",
"date_indexed": "-",
"serp": "-",
"bot_ip": "-"
},
{
"url": "https://example.com/page2",
"indexable": "200",
"date_created": "2026-05-22",
"status": "queued",
"date_indexed": "-",
"serp": "-",
"bot_ip": "-"
}
]
}
}Common Error Codes
order_not_found: The order ID does not exist.unauthorized: The current user does not own the order and does not have WooCommerce management capabilities.
Check Indexed
This separate controller in class-check-indexed.php lets authenticated users check indexed status for a batch of URLs.
POST https://www.indexceptional.com/wp-json/index/api/v1/check-indexedRequest Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| urls | string[] | Yes | Must contain between 1 and 500 valid URLs. |
| batch_name | string | No | Optional label stored with the batch history. |
The controller enforces a daily quota of 500 checked URLs per user, stored in a transient with a TTL of 86400 seconds.
Example Response
{
"success": true,
"data": {
"batch_id": "chk_664d1c0012345",
"indexed": 1,
"not_indexed": 1,
"items": [],
"quota_used_today": 2,
"quota_remaining": 498
}
}Common Error Codes
invalid_urls: The request body did not contain between 1 and 500 URLs.invalid_url: At least one URL failed validation.quota_exceeded: The daily indexed-check quota was exceeded.missing_dependency: The indexed status dependencies are unavailable.db_insert_failed: Check history could not be stored.
Response Formats
Success Response
{
"success": true,
"data": {
// Endpoint-specific payload
}
}Error Response
{
"code": "error_code",
"message": "Error message description",
"data": {
"status": 400
}
}API Restrictions
- Order submission limits:
500URLs per order,20seconds between submissions, and10orders per day. - Order product rules: only slugs
drip-indexandindexare accepted for credit-purchase indexing orders. - Indexed-check quota:
500URLs per user per86400-second quota window.
Error Responses
Too Many URLs
{
"code": "too_many_urls",
"message": "Maximum of 500 URLs allowed per order. You provided 750 URLs.",
"data": {
"status": 400
}
}Rate Limit Exceeded
{
"code": "rate_limit_exceeded",
"message": "Please wait 20 seconds before submitting another request.",
"data": {
"status": 429
}
}The actual wait time is dynamic. The code returns the remaining seconds until the 20-second minimum interval is satisfied.
Daily Limit Exceeded
{
"code": "daily_limit_exceeded",
"message": "Maximum of 10 orders per day reached. Please try again tomorrow.",
"data": {
"status": 429
}
}Status Code
| Status Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad Request |
| 403 | Forbidden |
| 404 | Not Found |
| 429 | Too Many Requests |
| 500 | Server Error |
Order Status Flow
The order list endpoint allows these status filters:
api-orderedmanual-actiongbot-visitedchecking-indexcompleted
Per-URL entries created by POST /order start at queued. The route code does not define a documented not-started status.
Best Practices
- Check
/check-balancebefore large submissions if your account is not unlimited. - Store the returned
order_idand poll/order/status/{order_id}when you need URL-level progress. - Pass
product_idexplicitly if you need theindextier. Omitting it defaults todrip-index. - Handle
400,403,404,429, and500responses explicitly. - Do not rely on rate-limit response headers. The current code enforces limits in PHP and returns errors when limits are hit.
Need Help?
For support or questions, contact us at:
- Email: [email protected]
- Website: https://www.indexceptional.com/support