API

API Documentation

Connect to Panelora from your own panel or app: place orders, check status, request refills and read your balance over a simple HTTP API.

API URL

https://demo.example/api/v2

Method

POST

Content type

application/json · form-data

Response format

JSON

Failures come back as HTTP 200 with an error field — always check the body, not just the status code. The key parameter is key.

Get balance

action=balance

Returns the current account balance.

NameTypeRequiredDescription
keystringYesYour API key
actionstringYesbalance
curl -X POST https://demo.example/api/v2 \
  -H "Content-Type: application/json" \
  -d '{"key":"YOUR_API_KEY","action":"balance"}'
<?php
$ch = curl_init('https://demo.example/api/v2');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'key' => 'YOUR_API_KEY',
    'action' => 'balance'
]));

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

print_r($response);
import requests

response = requests.post(
    "https://demo.example/api/v2",
    json={
        "key": "YOUR_API_KEY",
        "action": "balance"
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://demo.example/api/v2", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    "key": "YOUR_API_KEY",
    "action": "balance"
  }),
});

console.log(await response.json());

Response

{
  "balance": "100.84",
  "currency": "INR"
}

Get service list

action=services

Returns every active service with your personal pricing applied.

NameTypeRequiredDescription
keystringYesYour API key
actionstringYesservices
curl -X POST https://demo.example/api/v2 \
  -H "Content-Type: application/json" \
  -d '{"key":"YOUR_API_KEY","action":"services"}'
<?php
$ch = curl_init('https://demo.example/api/v2');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'key' => 'YOUR_API_KEY',
    'action' => 'services'
]));

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

print_r($response);
import requests

response = requests.post(
    "https://demo.example/api/v2",
    json={
        "key": "YOUR_API_KEY",
        "action": "services"
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://demo.example/api/v2", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    "key": "YOUR_API_KEY",
    "action": "services"
  }),
});

console.log(await response.json());

Response

[
  {
    "service": 8109,
    "name": "Instagram Likes | Real | No Refill | 2K/Day",
    "type": "Default",
    "category": "Instagram Likes",
    "rate": "33.7459",
    "min": 100,
    "max": 1000,
    "refill": true,
    "cancel": false,
    "dripfeed": true,
    "price": 33.7459,
    "category_id": 14
  }
]
  • The response is a plain array, with no wrapper object around it.
  • rate is the price per 1000 units in your account currency, with your personal pricing and any sale already applied.
  • type is Default or Custom Comments. A Custom Comments service needs the comments parameter on add.
  • refill, cancel and dripfeed tell you which of those the service supports.
  • price (numeric) and category_id are extras beyond the standard fields — ignore them if your importer does not expect them.

Place an order

action=add

Creates a new order and debits the charge from your balance immediately.

NameTypeRequiredDescription
keystringYesYour API key
actionstringYesadd
serviceintYesService ID
linkstringYesTarget link / profile URL
quantityintYesQuantity, within the service min/max
usernamestringNoUsername, for services that ask for one instead of a link
commentsstringNoCustom comments, one per line (comment services)
dripfeedintNoNumber of runs for drip-feed delivery
dripfeed_deliverystringNoDelivery interval for drip-feed
subscriptions_typeintNoSubscription type (subscription services)
subscriptions_expirystringNoSubscription expiry date, YYYY-MM-DD
curl -X POST https://demo.example/api/v2 \
  -H "Content-Type: application/json" \
  -d '{"key":"YOUR_API_KEY","action":"add","service":8109,"link":"https://instagram.com/p/AbCdEf","quantity":100}'
<?php
$ch = curl_init('https://demo.example/api/v2');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'key' => 'YOUR_API_KEY',
    'action' => 'add',
    'service' => 8109,
    'link' => 'https://instagram.com/p/AbCdEf',
    'quantity' => 100
]));

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

print_r($response);
import requests

response = requests.post(
    "https://demo.example/api/v2",
    json={
        "key": "YOUR_API_KEY",
        "action": "add",
        "service": 8109,
        "link": "https://instagram.com/p/AbCdEf",
        "quantity": 100
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://demo.example/api/v2", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    "key": "YOUR_API_KEY",
    "action": "add",
    "service": 8109,
    "link": "https://instagram.com/p/AbCdEf",
    "quantity": 100
  }),
});

console.log(await response.json());

Response

{
  "order": 23501
}
  • The charge is price × quantity ÷ 1000, rounded to the panel precision.
  • A quantity outside the service min/max, or a balance that will not cover the charge, comes back as { "error": "..." } and nothing is created — no order, no debit.

Order status

action=status

Returns the charge, start count and remaining quantity for one order.

NameTypeRequiredDescription
keystringYesYour API key
actionstringYesstatus
orderintYesOrder ID
curl -X POST https://demo.example/api/v2 \
  -H "Content-Type: application/json" \
  -d '{"key":"YOUR_API_KEY","action":"status","order":23501}'
<?php
$ch = curl_init('https://demo.example/api/v2');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'key' => 'YOUR_API_KEY',
    'action' => 'status',
    'order' => 23501
]));

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

print_r($response);
import requests

response = requests.post(
    "https://demo.example/api/v2",
    json={
        "key": "YOUR_API_KEY",
        "action": "status",
        "order": 23501
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://demo.example/api/v2", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    "key": "YOUR_API_KEY",
    "action": "status",
    "order": 23501
  }),
});

console.log(await response.json());

Response

{
  "order": 23501,
  "status": "In progress",
  "charge": "3.37",
  "start_count": "3572",
  "remains": "157",
  "currency": "INR"
}
  • status is the order state — see the table further down.
  • An id that is not on your account comes back as { "error": "Incorrect order ID" }.

Multiple order status

action=multi status

Status for up to 100 orders in a single call — the cheapest way to poll.

NameTypeRequiredDescription
keystringYesYour API key
actionstringYesmulti status (multi_status also works)
ordersstringYesOrder IDs separated by commas (max 100)
curl -X POST https://demo.example/api/v2 \
  -H "Content-Type: application/json" \
  -d '{"key":"YOUR_API_KEY","action":"multi status","orders":"23501,23502,23503"}'
<?php
$ch = curl_init('https://demo.example/api/v2');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'key' => 'YOUR_API_KEY',
    'action' => 'multi status',
    'orders' => '23501,23502,23503'
]));

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

print_r($response);
import requests

response = requests.post(
    "https://demo.example/api/v2",
    json={
        "key": "YOUR_API_KEY",
        "action": "multi status",
        "orders": "23501,23502,23503"
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://demo.example/api/v2", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    "key": "YOUR_API_KEY",
    "action": "multi status",
    "orders": "23501,23502,23503"
  }),
});

console.log(await response.json());

Response

{
  "23501": {
    "status": "Completed",
    "charge": "3.37",
    "start_count": "3572",
    "remains": "0",
    "currency": "INR"
  },
  "23502": {
    "status": "In progress",
    "charge": "1.12",
    "start_count": "980",
    "remains": "240",
    "currency": "INR"
  },
  "23503": {
    "error": "Incorrect order ID"
  }
}
  • Keyed by order id at the top level, with no wrapper object.
  • An id that is not yours comes back as { "error": "Incorrect order ID" } in its own slot — the call itself still succeeds.

Request a refill

action=refill

Queues a refill for an order on a refill-enabled service.

NameTypeRequiredDescription
keystringYesYour API key
actionstringYesrefill
orderintNoOrder ID — one order
ordersstringNoOrder IDs separated by commas, instead of order (max 100)
curl -X POST https://demo.example/api/v2 \
  -H "Content-Type: application/json" \
  -d '{"key":"YOUR_API_KEY","action":"refill","order":23501}'
<?php
$ch = curl_init('https://demo.example/api/v2');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'key' => 'YOUR_API_KEY',
    'action' => 'refill',
    'order' => 23501
]));

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

print_r($response);
import requests

response = requests.post(
    "https://demo.example/api/v2",
    json={
        "key": "YOUR_API_KEY",
        "action": "refill",
        "order": 23501
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://demo.example/api/v2", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    "key": "YOUR_API_KEY",
    "action": "refill",
    "order": 23501
  }),
});

console.log(await response.json());

Response

{
  "refill": 3
}
  • Keep the refill id — it is what refill status takes.
  • With orders instead of order the response is an array: [{ "order": 23501, "refill": 3 }, { "order": 23502, "refill": { "error": "Incorrect order ID" } }].
  • A service with no refill, or a refill window that has already closed, comes back as { "error": "..." }.

Refill status

action=refill status

State of a refill request.

NameTypeRequiredDescription
keystringYesYour API key
actionstringYesrefill status (refill_status also works)
refillintNoRefill ID, as returned by refill
refillsstringNoRefill IDs separated by commas, instead of refill (max 100)
orderintNoOrder ID — accepted as an alternative to refill
curl -X POST https://demo.example/api/v2 \
  -H "Content-Type: application/json" \
  -d '{"key":"YOUR_API_KEY","action":"refill status","refill":3}'
<?php
$ch = curl_init('https://demo.example/api/v2');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'key' => 'YOUR_API_KEY',
    'action' => 'refill status',
    'refill' => 3
]));

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

print_r($response);
import requests

response = requests.post(
    "https://demo.example/api/v2",
    json={
        "key": "YOUR_API_KEY",
        "action": "refill status",
        "refill": 3
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://demo.example/api/v2", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    "key": "YOUR_API_KEY",
    "action": "refill status",
    "refill": 3
  }),
});

console.log(await response.json());

Response

{
  "status": "Completed",
  "refill": 3
}
  • status is one of Pending, In progress, Completed, Rejected or Error.
  • With refills the response is an array: [{ "refill": 3, "status": "Completed" }, { "refill": 4, "status": { "error": "Incorrect refill ID" } }].

Cancel orders

action=cancel

Queues a cancellation request for orders that have not been delivered yet.

NameTypeRequiredDescription
keystringYesYour API key
actionstringYescancel
ordersstringYesOrder IDs separated by commas (max 100)
curl -X POST https://demo.example/api/v2 \
  -H "Content-Type: application/json" \
  -d '{"key":"YOUR_API_KEY","action":"cancel","orders":"23501,23502"}'
<?php
$ch = curl_init('https://demo.example/api/v2');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'key' => 'YOUR_API_KEY',
    'action' => 'cancel',
    'orders' => '23501,23502'
]));

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

print_r($response);
import requests

response = requests.post(
    "https://demo.example/api/v2",
    json={
        "key": "YOUR_API_KEY",
        "action": "cancel",
        "orders": "23501,23502"
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://demo.example/api/v2", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    "key": "YOUR_API_KEY",
    "action": "cancel",
    "orders": "23501,23502"
  }),
});

console.log(await response.json());

Response

[
  {
    "order": 23501,
    "cancel": 1
  },
  {
    "order": 23502,
    "cancel": {
      "error": "Incorrect order status"
    }
  }
]
  • One entry per order, in the order you asked for them. A single order is also accepted.
  • Only pending, processing and inprogress orders can be cancelled; anything else returns { "error": "Incorrect order status" } in its own slot.
  • The request is queued for processing — it is not an instant refund.

Order statuses

ValueMeaning
PendingAccepted, waiting to be sent to the provider.
ProcessingBeing prepared for delivery.
In progressDelivery is running.
CompletedFully delivered; remains is 0.
PartialPartly delivered; the undelivered part is refunded.
CanceledCancelled and refunded.
RefundedCharge returned to your balance.

Errors

CodeWhen it happens
200 OKThe normal answer, for both success and failure. Read the body: an error field means the call was refused and nothing was changed.
429 Too Many RequestsRate limit exceeded — back off and retry.
500 Server ErrorSomething failed on our side. Safe to retry with backoff.
Need help?