Reseller API
Order, check your balance and collect deliveries from your own panel, bot or script. One endpoint, four actions, JSON answers.
https://ogsub.net/api/v2
- Authentication
keyin the request body - FormatForm fields or JSON in, JSON out
- Rate limit60 requests a minute per key
- AmountsStrings in
EGP, two decimals
Getting started
- Sign in, open API keys and create a key. It is shown once, so copy it to your server right away.
- Optionally lock the key to your server’s IP addresses (up to 20). Requests from any other address are refused.
- Send
POSTrequests to the endpoint with yourkeyand anaction. Up to 5 keys can be active at once, and any of them can be revoked.
Request format
- Body: form fields (
application/x-www-form-urlencoded) or JSON withContent-Type: application/json. - Send the key in the body, never in the URL. Cookies and CSRF tokens are not used.
- Answers are JSON in English. Amounts are strings with two decimals in Egyptian pounds (
EGP), whatever currency the site shows. - Orders are paid from your wallet balance, so top up on the site before ordering.
Works with Perfect Panel API v2 clients: same parameter names (key, action, service, link, quantity, order, orders). Differences: every order is one unit (quantity 1), rate is the price of one order rather than per 1000, and there is no refill or cancel.
List services
POSTaction=servicesEverything on sale with its current price. Fetch it before ordering: prices and availability change. Only key and action.
Example
curl -X POST https://ogsub.net/api/v2 \
-d key=ogk_your_api_key \
-d action=services<?php
$ch = curl_init('https://ogsub.net/api/v2');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'key' => 'ogk_your_api_key',
'action' => 'services',
]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$response = json_decode(curl_exec($ch), true);
print_r($response);import requests
response = requests.post(
"https://ogsub.net/api/v2",
data={
"key": "ogk_your_api_key",
"action": "services",
},
timeout=30,
)
print(response.json())// Node.js 18+ (built-in fetch), as an ES module
const response = await fetch("https://ogsub.net/api/v2", {
method: "POST",
body: new URLSearchParams({
key: "ogk_your_api_key",
action: "services",
}),
});
console.log(await response.json());[
{
"service": 12,
"name": "ChatGPT Plus 1 Month",
"type": "Default",
"category": "ChatGPT",
"rate": "350.00",
"min": 1,
"max": 1,
"input": "email",
"available": true,
"refill": false,
"cancel": false
}
]Response fields
| Field | Description |
|---|---|
service | Service ID to send with add. |
name | Service name (English). |
category | Brand or category (English). |
rate | Price of one order in EGP. |
min max | Always 1: each order is one unit. |
input | What link must carry when you order: none, email or text. |
available | false while the service is paused or sold out. |
type refill cancel | Fixed (Default, false, false) for Perfect Panel clients. |
Check balance
POSTaction=balanceYour wallet balance. Only key and action.
Example
curl -X POST https://ogsub.net/api/v2 \
-d key=ogk_your_api_key \
-d action=balance<?php
$ch = curl_init('https://ogsub.net/api/v2');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'key' => 'ogk_your_api_key',
'action' => 'balance',
]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$response = json_decode(curl_exec($ch), true);
print_r($response);import requests
response = requests.post(
"https://ogsub.net/api/v2",
data={
"key": "ogk_your_api_key",
"action": "balance",
},
timeout=30,
)
print(response.json())// Node.js 18+ (built-in fetch), as an ES module
const response = await fetch("https://ogsub.net/api/v2", {
method: "POST",
body: new URLSearchParams({
key: "ogk_your_api_key",
action: "balance",
}),
});
console.log(await response.json());{
"balance": "1250.00",
"currency": "EGP"
}Response fields
| Field | Description |
|---|---|
balance | Balance in EGP. |
currency | Always EGP. |
Place an order
POSTaction=addPlaces one order and takes its price from your balance at once.
Parameters
| Parameter | Required | Description |
|---|---|---|
service | Yes | Service ID from services. |
link | Per service | What the service needs, by its input: email: the account email to activate; text: the details asked for (2–255 characters); none: leave it out. |
quantity | No | Leave it out or send 1. |
Idempotency-Key | Recommended Header | A unique string per order, up to 64 characters (for example a UUID). Sending the same value again returns the same order instead of placing and charging a second one. |
Example
curl -X POST https://ogsub.net/api/v2 \
-H "Idempotency-Key: 3f9c2a7e-order-1042" \
-d key=ogk_your_api_key \
-d action=add \
-d service=12 \
--data-urlencode [email protected]<?php
$ch = curl_init('https://ogsub.net/api/v2');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'key' => 'ogk_your_api_key',
'action' => 'add',
'service' => 12,
'link' => '[email protected]',
]),
CURLOPT_HTTPHEADER => ['Idempotency-Key: 3f9c2a7e-order-1042'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$response = json_decode(curl_exec($ch), true);
print_r($response);import requests
response = requests.post(
"https://ogsub.net/api/v2",
data={
"key": "ogk_your_api_key",
"action": "add",
"service": 12,
"link": "[email protected]",
},
headers={"Idempotency-Key": "3f9c2a7e-order-1042"},
timeout=30,
)
print(response.json())// Node.js 18+ (built-in fetch), as an ES module
const response = await fetch("https://ogsub.net/api/v2", {
method: "POST",
headers: { "Idempotency-Key": "3f9c2a7e-order-1042" },
body: new URLSearchParams({
key: "ogk_your_api_key",
action: "add",
service: "12",
link: "[email protected]",
}),
});
console.log(await response.json());{
"order": "OG4K7Q2XNM",
"charge": "350.00",
"currency": "EGP"
}Response fields
| Field | Description |
|---|---|
order | Order ID. Keep it to check the status. |
charge | Amount taken from your balance. |
currency | Always EGP. |
Coupons are not applied to API orders.
Order status
POSTaction=statusOne order with order, or up to 100 with orders. Only your own orders are found.
Parameters
| Parameter | Required | Description |
|---|---|---|
order | Yes | One order ID. |
orders | No | Up to 100 order IDs, comma-separated (instead of order). |
Example
curl -X POST https://ogsub.net/api/v2 \
-d key=ogk_your_api_key \
-d action=status \
-d order=OG4K7Q2XNM<?php
$ch = curl_init('https://ogsub.net/api/v2');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'key' => 'ogk_your_api_key',
'action' => 'status',
'order' => 'OG4K7Q2XNM',
]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$response = json_decode(curl_exec($ch), true);
print_r($response);import requests
response = requests.post(
"https://ogsub.net/api/v2",
data={
"key": "ogk_your_api_key",
"action": "status",
"order": "OG4K7Q2XNM",
},
timeout=30,
)
print(response.json())// Node.js 18+ (built-in fetch), as an ES module
const response = await fetch("https://ogsub.net/api/v2", {
method: "POST",
body: new URLSearchParams({
key: "ogk_your_api_key",
action: "status",
order: "OG4K7Q2XNM",
}),
});
console.log(await response.json());{
"status": "Completed",
"charge": "350.00",
"currency": "EGP",
"created": "2026-10-24 18:05:11Z",
"delivery": "Activated on [email protected]"
}Response fields
| Field | Description |
|---|---|
status | See the statuses below. |
charge | Amount taken from your balance. |
created | When it was placed, in UTC (YYYY-MM-DD HH:MM:SSZ). |
delivery | Only once Completed: what was delivered (often a login). Treat it as a secret. |
Several orders at once
With orders, every ID maps to its own result. Unknown IDs get {"error": "Incorrect order ID"} and the call still succeeds.
curl -X POST https://ogsub.net/api/v2 \
-d key=ogk_your_api_key \
-d action=status \
-d orders=OG4K7Q2XNM,OG9P3D5WLA<?php
$ch = curl_init('https://ogsub.net/api/v2');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'key' => 'ogk_your_api_key',
'action' => 'status',
'orders' => 'OG4K7Q2XNM,OG9P3D5WLA',
]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$response = json_decode(curl_exec($ch), true);
print_r($response);import requests
response = requests.post(
"https://ogsub.net/api/v2",
data={
"key": "ogk_your_api_key",
"action": "status",
"orders": "OG4K7Q2XNM,OG9P3D5WLA",
},
timeout=30,
)
print(response.json())// Node.js 18+ (built-in fetch), as an ES module
const response = await fetch("https://ogsub.net/api/v2", {
method: "POST",
body: new URLSearchParams({
key: "ogk_your_api_key",
action: "status",
orders: "OG4K7Q2XNM,OG9P3D5WLA",
}),
});
console.log(await response.json());{
"OG4K7Q2XNM": {
"status": "Completed",
"charge": "350.00",
"currency": "EGP",
"created": "2026-10-24 18:05:11Z",
"delivery": "Activated on [email protected]"
},
"OG9P3D5WLA": {
"error": "Incorrect order ID"
}
}Order statuses
| Status | Meaning |
|---|---|
| Pending | Paid and waiting to be processed. |
| In progress | Being prepared. |
| Completed | Delivered: delivery holds the details. |
| Canceled | Not delivered. The full charge is back in your balance. |
Auto-delivered services usually finish within minutes; the team handles the rest. Keep checking status for the orders you are waiting on.
Errors
A failed call answers with an HTTP status outside 2xx and an error message. Check the status code first.
| HTTP status | Meaning | Example messages |
|---|---|---|
| 400 | The request is incomplete or wrong. | Unknown actionInvalid serviceQuantity must be 1Missing orderIncorrect order ID |
| 401 | The key was not accepted. | Invalid API keyIP address not allowed for this keyAccount disabled |
| 403 | Your IP address is blocked by the store. | |
| 405 | The request was not a POST. | |
| 422 | The order cannot be placed now. | Insufficient balance.This service is sold out for now.This service is temporarily unavailable.Enter a valid account email.Enter the required order details.Ordering is paused right now. Please check back soon. |
| 429 | Too many requests. Slow down and retry later. | Too many requests |
| 500 | Server error. Retry later; if it repeats, send the ref value to support. | Server error |
HTTP/1.1 422 Unprocessable Entity
{
"error": "Insufficient balance."
}Limits and retries
- 60 requests per minute per key and 120 per minute per IP address. Above that the answer is
429. - If an
addcall times out or the connection drops, send it again with the sameIdempotency-Key: you get the order that was already placed, never a second charge. Without the header every call is a new order. - Check up to 100 orders in one
statuscall instead of one call per order, and wait 30–60 seconds between checks.
Keep the key safe
- The key spends your balance. Use it only on your server, never in an app, a web page or a public repository.
- Lock it to your server’s IP addresses when you can.
- If it leaks, revoke it on the API keys page and create a new one. The old key stops working at once.
deliveryholds what you bought (often a login). Store it as carefully as a password.
Full client example
Picks the first available service, orders it and waits for the delivery. The same flow in PHP, Python and Node.js: pick yours from the tabs.
<?php
const API = 'https://ogsub.net/api/v2';
const KEY = 'ogk_your_api_key'; // keep it on the server, e.g. in an environment variable
function og_api(array $params, array $headers = []): array
{
$ch = curl_init(API);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query(['key' => KEY] + $params),
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$data = is_string($body) ? json_decode($body, true) : null;
if ($code !== 200 || !is_array($data) || isset($data['error'])) {
throw new RuntimeException('API ' . $code . ': ' . ($data['error'] ?? 'no answer'));
}
return $data;
}
// 1. Pick the first available service
$services = og_api(['action' => 'services']);
$service = current(array_filter($services, fn (array $s): bool => $s['available']));
if (!$service) {
exit("Nothing available right now\n");
}
// "link" carries what the service's input asks for: nothing, the account email, or the details
$link = ['none' => '', 'email' => '[email protected]', 'text' => 'the details the service asks for'][$service['input']];
// 2. Order it. On a timeout, send the same Idempotency-Key again: never a second charge.
$idem = bin2hex(random_bytes(16));
$order = og_api(['action' => 'add', 'service' => $service['service'], 'link' => $link], ['Idempotency-Key: ' . $idem]);
// 3. Wait for the delivery
do {
sleep(30);
$status = og_api(['action' => 'status', 'order' => $order['order']]);
} while (in_array($status['status'], ['Pending', 'In progress'], true));
echo $status['status'] === 'Completed' ? $status['delivery'] : "Canceled: the charge is back in your balance\n";import os
import secrets
import time
import requests # pip install requests
API = "https://ogsub.net/api/v2"
KEY = os.environ.get("OG_API_KEY", "ogk_your_api_key") # keep it on the server
def og_api(params, headers=None):
r = requests.post(API, data={"key": KEY, **params}, headers=headers or {}, timeout=30)
try:
data = r.json()
except ValueError:
data = None
if r.status_code != 200 or data is None or (isinstance(data, dict) and "error" in data):
message = data.get("error", "no answer") if isinstance(data, dict) else "no answer"
raise RuntimeError(f"API {r.status_code}: {message}")
return data
# 1. Pick the first available service
services = og_api({"action": "services"})
service = next((s for s in services if s["available"]), None)
if service is None:
raise SystemExit("Nothing available right now")
# "link" carries what the service's input asks for: nothing, the account email, or the details
link = {"none": "", "email": "[email protected]", "text": "the details the service asks for"}[service["input"]]
# 2. Order it. On a timeout, send the same Idempotency-Key again: never a second charge.
idem = secrets.token_hex(16)
order = og_api({"action": "add", "service": service["service"], "link": link}, {"Idempotency-Key": idem})
# 3. Wait for the delivery
while True:
time.sleep(30)
status = og_api({"action": "status", "order": order["order"]})
if status["status"] not in ("Pending", "In progress"):
break
print(status["delivery"] if status["status"] == "Completed" else "Canceled: the charge is back in your balance")// Node.js 18+ (built-in fetch). Save as client.mjs and run: node client.mjs
import { randomUUID } from "node:crypto";
const API = "https://ogsub.net/api/v2";
const KEY = process.env.OG_API_KEY ?? "ogk_your_api_key"; // keep it on the server
async function ogApi(params, headers = {}) {
const res = await fetch(API, {
method: "POST",
headers,
body: new URLSearchParams({ key: KEY, ...params }),
signal: AbortSignal.timeout(30_000),
});
const data = await res.json().catch(() => null);
if (!res.ok || data === null || data.error) {
throw new Error(`API ${res.status}: ${data?.error ?? "no answer"}`);
}
return data;
}
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// 1. Pick the first available service
const services = await ogApi({ action: "services" });
const service = services.find((s) => s.available);
if (!service) {
console.log("Nothing available right now");
process.exit(0);
}
// "link" carries what the service's input asks for: nothing, the account email, or the details
const link = { none: "", email: "[email protected]", text: "the details the service asks for" }[service.input];
// 2. Order it. On a timeout, send the same Idempotency-Key again: never a second charge.
const order = await ogApi({ action: "add", service: String(service.service), link }, { "Idempotency-Key": randomUUID() });
// 3. Wait for the delivery
let status;
do {
await sleep(30_000);
status = await ogApi({ action: "status", order: order.order });
} while (["Pending", "In progress"].includes(status.status));
console.log(status.status === "Completed" ? status.delivery : "Canceled: the charge is back in your balance");