API docs
The REST API powers the web app, the open-source Go CLI at github.com/oppa26/ioncube-decode, and any integration you want to build. Async pattern: upload → poll status → download.
Base URL
https://console.decodephp.ioAuthentication
All authenticated endpoints use the X-API-Key header. Your API key is your 16-digit account number, available in the console dashboard. API access is included for every account, and API decodes use the same credits as browser uploads.
X-API-Key: 1234567890123456Failed API-key attempts are rate-limited: 5 failures per minute per IP returns 429 Too Many Requests.
End-to-end flow
A decode is three calls:
- Upload the file — returns a
decode_idand queues the job. - Poll status — returns
pending,success, orfailed. Poll roughly every 500 ms. - Download the decoded bytes when status is
success.
POST /accounts/upload/
Multipart form upload. Field name: file. Max size 10 MB. Must be a .php file.
curl https://console.decodephp.io/accounts/upload/ \
-H "X-API-Key: 1234567890123456" \
-F "file=@encoded.php"Response (credits available, decode queued):
{
"success": true,
"message": "File queued for decoding.",
"decode_id": 4821,
"task_id": "a8b2c9d4-1234-5678-abcd-ef0123456789"
}Response when out of credits (free-tier preview fallback):
HTTP/1.1 402 Payment Required
{
"success": false,
"preview": true,
"message": "Preview only - purchase credits to download full file.",
"preview_lines": ["<?php", "namespace App;", "..."],
"total_lines": 240,
"file_name": "encoded.php"
}Error responses:
400 — No file provided / not a .php / file too large (>10MB)
400 — File is not IonCube encoded
400 — PHP version not supported
401 — Invalid API key
429 — Rate limit (5 failed API keys / minute)GET /accounts/decode/<decode_id>/status/
Poll until status is success or failed.
curl https://console.decodephp.io/accounts/decode/4821/status/ \
-H "X-API-Key: 1234567890123456"Response while pending:
{
"success": true,
"decode_id": 4821,
"status": "pending",
"original_filename": "encoded.php",
"php_version": "8.4",
"ioncube_version": "15"
}Response on success:
{
"success": true,
"decode_id": 4821,
"status": "success",
"original_filename": "encoded.php",
"php_version": "8.4",
"ioncube_version": "15",
"decoded_size": 8912,
"processing_time_ms": 1420,
"download_url": "/accounts/decode/4821/download/",
"view_url": "/accounts/decode/4821/view/",
"opcodes_url": "/accounts/decode/4821/opcodes/"
}Response on failure:
{
"success": true,
"decode_id": 4821,
"status": "failed",
"original_filename": "encoded.php",
"php_version": "",
"ioncube_version": "",
"error_message": "Could not identify IonCube version"
}GET /accounts/decode/<decode_id>/download/
Returns the decoded PHP source as application/x-php with a Content-Disposition: attachment header. Filename is <original>_decoded.php.
curl -OJ https://console.decodephp.io/accounts/decode/4821/download/ \
-H "X-API-Key: 1234567890123456"Error responses:
402 — Purchase credits to download (free-tier decode, preview only)
400 — Decoded file not available
404 — Not found / not owned by your accountGET /accounts/decode/<decode_id>/view/
Renders the decoded source in a browser viewer instead of downloading. Requires credits.
GET /accounts/decode/<decode_id>/opcodes/
Downloads the pretty-printed Zend opcode disassembly for the file. Useful for advanced reverse engineering. Requires credits.
GET /accounts/credits/
Current credit balance.
curl https://console.decodephp.io/accounts/credits/ \
-H "X-API-Key: 1234567890123456"{
"available_credits": 47,
"lifetime_remaining": null,
"lifetime_limit": null,
"purchased_credits": 47,
"is_lifetime": false,
"is_subscriber": false,
"subscription_remaining": null,
"subscription_limit": null
}POST /accounts/preview/ (public)
Public endpoint used by the landing-page demo. Returns the first 20 lines of decoded output. Rate-limited to 30 preview requests per hour per IP.
The open-source Go CLI
The canonical reference implementation of this API is the Go CLI we publish on GitHub. It does upload, poll, download with a worker pool, and handles non-IonCube file skip detection.
# Install
go install github.com/oppa26/ioncube-decode@latest
# Single file
IONCUBE_API_KEY=1234567890123456 ioncube-decode encoded.php
# Directory (recursive), 8 concurrent workers, overwrite originals
IONCUBE_API_KEY=1234567890123456 ioncube-decode -w 8 -o ./src
Source: github.com/oppa26/ioncube-decode. MIT licensed.
Language examples
All examples assume DECODEPHP_KEY is your 16-digit API key.
PHP
<?php
$key = getenv('DECODEPHP_KEY');
$base = 'https://console.decodephp.io';
// 1. Upload
$ch = curl_init($base . '/accounts/upload/');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-API-Key: ' . $key],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => ['file' => new CURLFile('encoded.php')],
CURLOPT_RETURNTRANSFER => true,
]);
$uploaded = json_decode(curl_exec($ch), true);
$id = $uploaded['decode_id'];
// 2. Poll
while (true) {
$ch = curl_init($base . "/accounts/decode/{$id}/status/");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-API-Key: ' . $key],
CURLOPT_RETURNTRANSFER => true,
]);
$s = json_decode(curl_exec($ch), true);
if ($s['status'] === 'success') break;
if ($s['status'] === 'failed') exit('decode failed: ' . $s['error_message']);
usleep(500000);
}
// 3. Download
$ch = curl_init($base . "/accounts/decode/{$id}/download/");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-API-Key: ' . $key],
CURLOPT_RETURNTRANSFER => true,
]);
file_put_contents('decoded.php', curl_exec($ch));Python
import os, time, requests
key = os.environ['DECODEPHP_KEY']
base = 'https://console.decodephp.io'
headers = {'X-API-Key': key}
# 1. Upload
with open('encoded.php', 'rb') as f:
up = requests.post(
f'{base}/accounts/upload/',
headers=headers,
files={'file': f},
).json()
decode_id = up['decode_id']
# 2. Poll
while True:
s = requests.get(
f'{base}/accounts/decode/{decode_id}/status/',
headers=headers,
).json()
if s['status'] == 'success':
break
if s['status'] == 'failed':
raise RuntimeError(s['error_message'])
time.sleep(0.5)
# 3. Download
r = requests.get(
f'{base}/accounts/decode/{decode_id}/download/',
headers=headers,
)
with open('decoded.php', 'wb') as out:
out.write(r.content)Node.js
import fs from 'node:fs';
const key = process.env.DECODEPHP_KEY;
const base = 'https://console.decodephp.io';
const headers = { 'X-API-Key': key };
// 1. Upload
const form = new FormData();
form.append('file', new Blob([fs.readFileSync('encoded.php')]), 'encoded.php');
const upRes = await fetch(`${base}/accounts/upload/`, {
method: 'POST', headers, body: form,
});
const { decode_id } = await upRes.json();
// 2. Poll
while (true) {
const s = await (await fetch(
`${base}/accounts/decode/${decode_id}/status/`,
{ headers },
)).json();
if (s.status === 'success') break;
if (s.status === 'failed') throw new Error(s.error_message);
await new Promise(r => setTimeout(r, 500));
}
// 3. Download
const dl = await fetch(
`${base}/accounts/decode/${decode_id}/download/`,
{ headers },
);
fs.writeFileSync('decoded.php', Buffer.from(await dl.arrayBuffer()));Limits
- Maximum file size: 10 MB.
- Public preview: 30 requests / hour / IP.
- Failed API-key attempts: 5 / minute / IP.
- Concurrent decodes: bounded by your credit balance. The CLI default is 4 parallel workers — safe and fast.
Error codes
400 — Bad request (missing file, wrong MIME, too large, not IonCube, unsupported PHP)
401 — Invalid API key
402 — Out of credits (free-tier preview returned instead)
404 — Decode not found or not owned by your account
429 — Rate-limited
500 — Internal error (retry)
504 — Processing timed out (opcode export only)