Bills Payment
Pay airtime, data, cable TV, electricity, betting, and more. Single and bulk operations supported.
Airtime
Single Airtime
Purchase airtime for a single phone number.
Endpoint
POST /airtime
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
phone | string | Yes | Phone number to credit. Must include country code (e.g., 2348012345678). |
amount | integer | Yes | Amount of airtime to purchase in Naira. |
network | string | Yes | Network provider code. |
ref | string | No | Optional unique reference for the transaction; used to detect duplicate submissions. |
Supported Networks: MTNNG, AIRTELNG, GLO, 9MOBILE
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/airtime \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "2348012345678",
"amount": 500,
"network": "MTNNG",
"ref": "TXN-AIRTIME-001"
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/airtime', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"phone": "2348012345678",
"amount": 500,
"network": "MTNNG"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"phone": "2348012345678",
"amount": 500,
"network": "MTNNG"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/airtime',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"phone": "2348012345678",
"amount": 500,
"network": "MTNNG"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/airtime", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Order received. Transaction in progress.",
"message": {
"details": {
"processed": [
{
"number": "08012345678",
"amount": 500,
"id": "c3f1e2b0-5a6d-4c8f-9b1a-2d7e4f6a8c30",
"response": "Sandbox airtime delivered"
}
],
"transaction_status": "processing",
"ref": "TXN-AIRTIME-001",
"category": "airtime",
"product": "mtn",
"amount": 500,
"total_charge": 500,
"discount": 0,
"trans_id": "b7e4b1d2-6c9a-4f3b-8d2e-1a5c9f7b3e60",
"created_at": "2026-07-20 11:30:00"
}
},
"status_code": 200
}
transaction_status starts at processing; poll GET /requery?trans_id=... or listen for the bills.created/bills.status webhooks to see the final state. The phone number is normalized to local format (0...) in the response even when you send it with a country code.
Bulk Airtime
Send airtime to multiple numbers in one request using the same POST /airtime endpoint.
Endpoint
POST /airtime
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
Pass a rows array where each entry is a destination number. Every row requires phone, amount, and network.
| Parameter | Type | Required | Description |
|---|---|---|---|
rows | array | Yes | Array of destination objects containing phone, amount, and network. |
ref | string | No | Optional unique reference for the whole bulk request. |
Row Object:
| Parameter | Type | Required | Description |
|---|---|---|---|
phone | string | Yes | Phone number to credit. |
amount | integer | Yes | Amount of airtime to purchase. |
network | string | Yes | Network provider code. |
reference | string | No | Optional per-row reference; falls back to ref. |
Alternatively, send phone as an array with a shared amount and network to credit every number with the same value.
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/airtime \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ref": "TXN-BULK-001",
"rows": [
{"phone": "2348012345678", "amount": 500, "network": "MTNNG"},
{"phone": "2348098765432", "amount": 300, "network": "AIRTELNG"}
]
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/airtime', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"ref": "TXN-BULK-001",
"rows": [
{
"phone": "2348012345678",
"amount": 500,
"network": "MTNNG"
},
{
"phone": "2348098765432",
"amount": 300,
"network": "AIRTELNG"
}
]
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"ref": "TXN-BULK-001",
"rows": [
{
"phone": "2348012345678",
"amount": 500,
"network": "MTNNG"
},
{
"phone": "2348098765432",
"amount": 300,
"network": "AIRTELNG"
}
]
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/airtime',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"ref": "TXN-BULK-001",
"rows": [
{
"phone": "2348012345678",
"amount": 500,
"network": "MTNNG"
},
{
"phone": "2348098765432",
"amount": 300,
"network": "AIRTELNG"
}
]
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/airtime", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Order received. Transaction in progress.",
"message": {
"details": {
"processed": [
{
"number": "08012345678",
"amount": 500,
"id": "e4b1c9d2-3a7f-4e6b-9c2a-1d8f5b3e6a90",
"response": "Sandbox airtime delivered"
},
{
"number": "08098765432",
"amount": 300,
"id": "f5c2d0e3-4b8a-4f7c-0d3b-2e9a6c4f7b01",
"response": "Sandbox airtime delivered"
}
],
"transaction_status": "processing",
"ref": "TXN-BULK-001",
"category": "airtime",
"product": "mtn",
"amount": 500,
"total_charge": 800,
"discount": 0,
"trans_id": "a3f2c9d1-7e4b-4f2a-9c3e-1b8d2f6a0c42",
"created_at": "2026-07-20 11:30:00"
}
},
"status_code": 200
}
Data Bundle
Lookup Data Plans
Retrieve available data plans for a given network.
Endpoint
GET /data/lookup?network=MTNNG
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
network | string | Yes | Network provider code (query parameter). |
Request
- cURL
- Node.js
- Python
- Go
curl -X GET "https://sandbox.payscribe.ng/api/v1/data/lookup?network=MTNNG" \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/data/lookup?network=MTNNG', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/data/lookup?network=MTNNG',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/data/lookup?network=MTNNG", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "MTNNG data plan lookup successfully.",
"message": {
"details": [
{
"network_name": "MTNNG",
"title": "MTN Data",
"network_code": "PSDATA_1",
"check_balance": "",
"logo_url": "https://cdn.payscribe.ng/logos/mtn.png",
"plans": [
{
"category": "Gifting",
"plans": [
{
"plan_code": "PSPLAN_12",
"name": "1GB Daily - Gifting",
"alias": "mtn-1gb",
"amount": 200,
"discount": 0,
"discount_type": "percentage",
"capped_at": null
},
{
"plan_code": "PSPLAN_13",
"name": "2GB Weekly - Gifting",
"alias": "mtn-2gb",
"amount": 500,
"discount": 0,
"discount_type": "percentage",
"capped_at": null
}
]
}
]
}
]
},
"status_code": 200
}
Use the plan_code value as variation when calling POST /data/vend.
Vend Data
Purchase a data bundle for a phone number.
Endpoint
POST /data/vend
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
phone | string | Yes | Phone number to credit. |
variation | string | Yes | Numeric plan id from the lookup endpoint's plan_code (strip the PSPLAN_ prefix — e.g. plan_code: "PSPLAN_12" becomes variation: "12"). |
provider | string | Yes | Network provider code. |
ref | string | Yes | Unique reference for the transaction. Unlike airtime, this endpoint rejects the request with a 406 if ref is missing. |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/data/vend \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "2348012345678",
"variation": "12",
"provider": "MTNNG",
"ref": "TXN-DATA-001"
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/data/vend', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"phone": "2348012345678",
"variation": "12",
"provider": "MTNNG",
"ref": "TXN-DATA-001"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"phone": "2348012345678",
"variation": "12",
"provider": "MTNNG",
"ref": "TXN-DATA-001"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/data/vend',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"phone": "2348012345678",
"variation": "12",
"provider": "MTNNG",
"ref": "TXN-DATA-001"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/data/vend", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Order received. Transaction in progress.",
"message": {
"details": {
"processed": [
{
"number": "2348012345678",
"amount": 500,
"id": "d1a2b3c4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"response": "Sandbox data vended successfully."
}
],
"transaction_status": "success",
"product": "MTNNG",
"ref": "TXN-DATA-001",
"amount": 500,
"total_charge": 500,
"discount": 0,
"trans_id": "e2b3c4d5-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
"created_at": "2026-07-20 11:30:00"
}
},
"status_code": 200
}
Cable TV
Fetch Bouquets
Retrieve available bouquets for a cable TV service.
Endpoint
GET /bouquets?service=dstv
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
service | string | Yes | Cable TV service provider (query parameter). |
Supported Services: dstv, gotv, startimes
Request
- cURL
- Node.js
- Python
- Go
curl -X GET "https://sandbox.payscribe.ng/api/v1/bouquets?service=dstv" \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/bouquets?service=dstv', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/bouquets?service=dstv',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/bouquets?service=dstv", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "DSTV lookup successfully.",
"message": {
"details": [
{
"id": "U2FsdGVkX18a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9==",
"alias": "dstv-compact",
"name": "DStv Compact",
"amount": 5000,
"priceOptions": []
},
{
"id": "U2FsdGVkX19b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0==",
"alias": "dstv-premium",
"name": "DStv Premium",
"amount": 10000,
"priceOptions": []
}
]
},
"status_code": 200
}
id is an opaque encrypted token, not the plan name — pass it back verbatim as plan_id when calling POST /multichoice/vend.
Validate Smart Card / IUC
Validate a smart card or IUC number for a cable TV service.
Endpoint
POST /multichoice/validate
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
service | string | Yes | Cable TV service provider (dstv, gotv, startimes). |
account | string | Yes | Smart card or IUC number. |
plan_id | string | Yes | Bouquet/plan ID returned by the bouquets endpoint. |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/multichoice/validate \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"service": "dstv", "account": "1234567890", "plan_id": "PLAN_123"}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/multichoice/validate', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"service": "dstv",
"account": "1234567890",
"plan_id": "PLAN_123"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"service": "dstv",
"account": "1234567890",
"plan_id": "PLAN_123"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/multichoice/validate',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"service": "dstv",
"account": "1234567890",
"plan_id": "PLAN_123"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/multichoice/validate", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Validation successful",
"message": {
"details": {
"customer_name": "ADEBAYO MOSUNMOLA",
"info": {
"outstandingBalance": 4850,
"accountNumber": "1234567890",
"currentBouquet": "GOtv Jolli Bouquet",
"currentBouquetPlan": {
"items": [
{
"code": "GOTVNJ2",
"name": "GOtv Jolli Bouquet",
"price": 4850,
"description": "GOtv Jolli Bouquet"
}
],
"amount": 4850
}
}
}
},
"status_code": 200
}
The sandbox always returns this fixed customer/bouquet regardless of the account you send.
Subscribe (Pay Cable TV)
Subscribe to a cable TV bouquet.
Endpoint
POST /multichoice/vend
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
service | string | Yes | Cable TV service provider. |
account | string | Yes | Smart card or IUC number. |
plan_id | string | Yes | The opaque id value returned by the bouquets endpoint. |
customer_name | string | No | Accepted but not currently used by the API — safe to omit. |
ref | string | Yes | Unique reference for the subscription. |
month | integer | No | Number of months to subscribe for. Defaults to 1. |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/multichoice/vend \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"service": "dstv",
"account": "1234567890",
"plan_id": "PLAN_123",
"customer_name": "John Doe",
"ref": "TXN-TV-001"
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/multichoice/vend', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"service": "dstv",
"account": "1234567890",
"plan_id": "PLAN_123",
"customer_name": "John Doe",
"ref": "TXN-TV-001"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"service": "dstv",
"account": "1234567890",
"plan_id": "PLAN_123",
"customer_name": "John Doe",
"ref": "TXN-TV-001"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/multichoice/vend',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"service": "dstv",
"account": "1234567890",
"plan_id": "PLAN_123",
"customer_name": "John Doe",
"ref": "TXN-TV-001"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/multichoice/vend", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "DSTV payment processed.",
"message": {
"details": {
"trans_id": "f1a2b3c4-5d6e-4f7a-8b9c-0d1e2f3a4b5c",
"transaction_status": "success",
"account": "1234567890",
"service": "dstv",
"plan": "GOTVNJ2",
"plan_id": "U2FsdGVkX18a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9==",
"ref": "TXN-TV-001",
"amount": 4850,
"discount": 0,
"total_charged": 4850,
"currency": "NGN",
"created_at": "2026-07-20 11:30:00"
}
},
"status_code": 200
}
ref is required — omitting it returns a 406.
Topup GoTV / DSTV
Add funds to a GoTV or DSTV account.
Endpoint
POST /multichoice/topup
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
service | string | Yes | Cable TV service provider (gotv, dstv). |
account | string | Yes | Smart card or IUC number. |
amount | integer | Yes | Amount to top up. |
customer_name | string | Yes | Name on the smart card account. |
ref | string | Yes | Your unique transaction reference. |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/multichoice/topup \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"service": "gotv",
"account": "1234567890",
"amount": 5000,
"customer_name": "John Doe",
"ref": "GOTV-TOPUP-001"
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/multichoice/topup', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"service": "gotv",
"account": "1234567890",
"amount": 5000,
"customer_name": "John Doe",
"ref": "GOTV-TOPUP-001"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"service": "gotv",
"account": "1234567890",
"amount": 5000,
"customer_name": "John Doe",
"ref": "GOTV-TOPUP-001"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/multichoice/topup',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"service": "gotv",
"account": "1234567890",
"amount": 5000,
"customer_name": "John Doe",
"ref": "GOTV-TOPUP-001"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/multichoice/topup", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "GOTV payment successful.",
"message": {
"details": {
"trans_id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"account": "1234567890",
"service": "gotv",
"plan": "topup",
"plan_id": null,
"ref": "GOTV-TOPUP-001",
"amount": 5000,
"discount": 0,
"total_charged": 5000,
"currency": "NGN",
"created_at": "2026-07-20 11:30:00"
}
},
"status_code": 200
}
Unlike Subscribe, a top-up isn't tied to a bouquet plan — plan is always "topup" and plan_id is always null. ref is required here too — omitting it returns a 406.
Electricity
Validate Meter
Validate a prepaid or postpaid meter number.
Endpoint
POST /electricity/validate
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
service | string | Yes | Distribution company code (e.g., ikejaelectric). |
meter_number | string | Yes | Meter number to validate. |
meter_type | string | Yes | Meter type (prepaid or postpaid). |
amount | integer | Yes | Amount to purchase (used to check minimum/debt). |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/electricity/validate \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"service": "ikejaelectric", "meter_number": "1234567890", "meter_type": "prepaid", "amount": 5000}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/electricity/validate', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"service": "ikejaelectric",
"meter_number": "1234567890",
"meter_type": "prepaid",
"amount": 5000
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"service": "ikejaelectric",
"meter_number": "1234567890",
"meter_type": "prepaid",
"amount": 5000
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/electricity/validate',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"service": "ikejaelectric",
"meter_number": "1234567890",
"meter_type": "prepaid",
"amount": 5000
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/electricity/validate", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Validation successful",
"message": {
"details": {
"customer_name": "John Doe",
"address": "12 Example Street, Lagos",
"outstanding_balance": 0,
"account_number": "1234567890",
"minimum_amount": 1000,
"debt_amount": 0,
"minimum_debt": 0
}
},
"status_code": 200
}
Pay Electricity
Make an electricity bill payment.
Endpoint
POST /electricity/vend
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
service | string | Yes | Distribution company code. |
meter_number | string | Yes | Meter number. |
meter_type | string | Yes | Meter type (prepaid or postpaid). |
amount | integer | Yes | Payment amount. |
phone | string | Yes | Phone number of the customer. |
customer_name | string | No | Name on the meter account; echoed back in the response but not required. |
ref | string | No | Optional unique reference for the payment; used to detect duplicate submissions when provided. |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/electricity/vend \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"service": "ikejaelectric",
"meter_number": "1234567890",
"meter_type": "prepaid",
"amount": 5000,
"phone": "2348012345678",
"customer_name": "John Doe",
"ref": "TXN-ELE-001"
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/electricity/vend', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"service": "ikejaelectric",
"meter_number": "1234567890",
"meter_type": "prepaid",
"amount": 5000,
"phone": "2348012345678",
"customer_name": "John Doe",
"ref": "TXN-ELE-001"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"service": "ikejaelectric",
"meter_number": "1234567890",
"meter_type": "prepaid",
"amount": 5000,
"phone": "2348012345678",
"customer_name": "John Doe",
"ref": "TXN-ELE-001"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/electricity/vend',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"service": "ikejaelectric",
"meter_number": "1234567890",
"meter_type": "prepaid",
"amount": 5000,
"phone": "2348012345678",
"customer_name": "John Doe",
"ref": "TXN-ELE-001"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/electricity/vend", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "IKEJAELECTRIC payment success",
"message": {
"details": {
"trans_id": "a2b3c4d5-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
"transaction_status": "success",
"ref": "TXN-ELE-001",
"created_at": "2026-07-20 11:30:00",
"token": "12345-12345-12345-12345",
"token_amount": 5000,
"unit": 57.8,
"amount_of_power": 57.8,
"reset_token": null,
"configure_token": null,
"tax_amount": 0,
"tariff": null,
"meter_number": "1234567890",
"plan": "prepaid",
"service": "ikejaelectric",
"customer_name": "John Doe",
"address": null
}
},
"status_code": 200
}
For a prepaid meter, token is the actual electricity recharge token — it's the field a customer needs, not trans_id.
ePins
List Available ePins
Retrieve all available ePin products.
Endpoint
GET /epins
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Request
- cURL
- Node.js
- Python
- Go
curl -X GET https://sandbox.payscribe.ng/api/v1/epins \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/epins', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/epins',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/epins", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Epins lookup successfully.",
"message": {
"details": [
{
"name": "Result Checkers",
"collection": [
{
"id": "waec",
"name": "WAEC Result Checker",
"amount": 3400,
"available": 1
},
{
"id": "neco",
"name": "NECO Result Checker",
"amount": 1300,
"available": 1
}
]
},
{
"name": "Registration Pins",
"collection": [
{
"id": "jamb-utme",
"name": "JAMB UTME Registration Pin",
"amount": 6200,
"available": 1
}
]
}
]
},
"status_code": 200
}
Use the id value (e.g. waec) as id when calling POST /epins/vend.
Purchase ePin
Purchase an electronic pin.
Endpoint
POST /epins/vend
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | ePin category id or slug from the list endpoint (e.g. waec). |
qty | integer | Yes | Number of pins to purchase. |
account | string | Conditional | JAMB registration number — required unless id is waec or neco. |
phone | string | Conditional | Phone number — required for JAMB pins. |
ref | string | No | Optional unique reference; used to detect duplicate submissions when provided. |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/epins/vend \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id": "waec", "qty": 1, "ref": "TXN-EPIN-001"}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/epins/vend', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"id": "waec",
"qty": 1,
"ref": "TXN-EPIN-001"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"id": "waec",
"qty": 1,
"ref": "TXN-EPIN-001"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/epins/vend',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"id": "waec",
"qty": 1,
"ref": "TXN-EPIN-001"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/epins/vend", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "ePin transaction successful",
"message": {
"details": {
"trans_id": "b3c4d5e6-7f8a-4b9c-0d1e-2f3a4b5c6d7e",
"transaction_status": "success",
"ref": "TXN-EPIN-001",
"qty": 1,
"amount": 3400,
"discount": 68,
"total_charge": 3332,
"title": "WAEC Result Checker",
"epins": [
{
"name": "WAEC Result Checker",
"pin": "123456789012",
"serial": "SBX483920",
"instruction": "Sandbox pin generated successfully"
}
]
}
},
"status_code": 200
}
Retrieve ePin
Retrieve a purchased ePin by transaction reference.
Endpoint
GET /epins/retrieve?trans_id=txn_abc123
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
trans_id | string | Yes | Transaction reference from a previous purchase (query parameter). |
Request
- cURL
- Node.js
- Python
- Go
curl -X GET "https://sandbox.payscribe.ng/api/v1/epins/retrieve?trans_id=txn_abc123" \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/epins/retrieve?trans_id=txn_abc123', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/epins/retrieve?trans_id=txn_abc123',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/epins/retrieve?trans_id=txn_abc123", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "ePin Fetched successful",
"message": {
"details": {
"trans_id": "txn_abc123",
"ref": "TXN-EPIN-001",
"qty": 1,
"amount": 3400,
"total_charge": 3400,
"epins": [
{
"name": "WAEC Result Checker",
"pin": "123456789012",
"serial": "SBX483920",
"instruction": "Sandbox pin generated successfully"
}
]
}
},
"status_code": 200
}
trans_id must belong to a transaction on your own account — looking up another business's trans_id returns a 404.
JAMB Lookup
Look up a JAMB user by profile code.
Endpoint
POST /epins/jamb/user/lookup
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | ePin category id or slug for the JAMB product (e.g. jamb-utme). |
account | string | Yes | JAMB registration/profile number to look up. |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/epins/jamb/user/lookup \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id": "jamb-utme", "account": "1234567890"}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/epins/jamb/user/lookup', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"id": "jamb-utme",
"account": "1234567890"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"id": "jamb-utme",
"account": "1234567890"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/epins/jamb/user/lookup',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"id": "jamb-utme",
"account": "1234567890"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/epins/jamb/user/lookup", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Jamb user lookup successful",
"message": {
"details": {
"account": "1234567890",
"name": "TEST JAMB USER"
}
},
"status_code": 200
}
Betting
List Providers
Retrieve available betting providers.
Endpoint
GET /betting/list
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Request
- cURL
- Node.js
- Python
- Go
curl -X GET https://sandbox.payscribe.ng/api/v1/betting/list \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/betting/list', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/betting/list',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/betting/list", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Betting service provider fetched successfully",
"message": {
"details": [
{
"id": "bet9ja",
"title": "Bet9ja",
"active": true
},
{
"id": "sportybet",
"title": "SportyBet",
"active": true
}
]
},
"status_code": 200
}
Validate Bet Account
Look up a betting customer account.
Endpoint
GET /betting/lookup?customer_id=123&bet_id=ABC
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
customer_id | string | Yes | Customer identifier (query parameter). |
bet_id | string | Yes | Betting provider id from GET /betting/list (query parameter). |
Request
- cURL
- Node.js
- Python
- Go
curl -X GET "https://sandbox.payscribe.ng/api/v1/betting/lookup?customer_id=123&bet_id=ABC" \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/betting/lookup?customer_id=123&bet_id=ABC', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/betting/lookup?customer_id=123&bet_id=ABC',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/betting/lookup?customer_id=123&bet_id=ABC", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Validation successful",
"message": {
"details": {
"name": "TEST BET USER",
"account": "123"
}
},
"status_code": 200
}
Fund Wallet
Fund a betting wallet.
Endpoint
POST /betting/vend
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
customer_id | string | Yes | Customer identifier. |
bet_id | string | Yes | Betting provider id from GET /betting/list (e.g. bet9ja). |
amount | integer | Yes | Amount to fund. Minimum 200. |
customer_name | string | No | Accepted but not currently used by the API — safe to omit. |
ref | string | No | Optional unique reference; used to detect duplicate submissions when provided. |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/betting/vend \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "123",
"bet_id": "bet9ja",
"amount": 1000,
"ref": "TXN-BET-001"
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/betting/vend', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"customer_id": "123",
"bet_id": "bet9ja",
"amount": 1000,
"ref": "TXN-BET-001"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"customer_id": "123",
"bet_id": "bet9ja",
"amount": 1000,
"ref": "TXN-BET-001"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/betting/vend',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"customer_id": "123",
"bet_id": "bet9ja",
"amount": 1000,
"ref": "TXN-BET-001"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/betting/vend", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "BET9JA payment successful.",
"message": {
"details": {
"trans_id": "c4d5e6f7-8a9b-4c0d-1e2f-3a4b5c6d7e8f",
"transaction_status": "success",
"ref": "TXN-BET-001",
"account": "123",
"bet_id": "bet9ja",
"created_at": "2026-07-20 11:30:00"
}
},
"status_code": 200
}
Note amount is not echoed back in this response — reconcile funded amounts against what you sent, not against the response body.
Bulk SMS
Send bulk SMS messages.
Endpoint
POST /sms
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
to | string or array | Yes | Phone number(s) to send to. A single string sends one SMS; an array sends to multiple numbers. Use 11-digit local format (for example 08012345678) — 13-digit country-code format (234...) is rejected by validation. |
message | string | Yes | SMS message content. |
ref | string | Yes | Unique reference to prevent duplicate sends. |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/sms \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": ["08012345678", "08098765432"],
"message": "Your payment has been received.",
"ref": "TXN-SMS-001"
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/sms', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"to": [
"08012345678",
"08098765432"
],
"message": "Your payment has been received.",
"ref": "TXN-SMS-001"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"to": [
"08012345678",
"08098765432"
],
"message": "Your payment has been received.",
"ref": "TXN-SMS-001"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/sms',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"to": [
"08012345678",
"08098765432"
],
"message": "Your payment has been received.",
"ref": "TXN-SMS-001"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/sms", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Order received. Transaction in progress.",
"message": {
"details": {
"processed": [
{
"uid": 4821,
"message_id": "5f4c2b1a-9d8e-4f6a-b3c2-1e0d9a8b7c6f",
"bulk_id": 1784620800,
"sender": "Pscribe",
"recipent": "+2348012345678",
"message": "Your payment has been received.",
"time_created": "2026-07-20 11:30:00",
"status": "success",
"page": 1,
"description": ""
},
{
"uid": 4821,
"message_id": "5f4c2b1a-9d8e-4f6a-b3c2-1e0d9a8b7c6f",
"bulk_id": 1784620800,
"sender": "Pscribe",
"recipent": "+2348098765432",
"message": "Your payment has been received.",
"time_created": "2026-07-20 11:30:00",
"status": "success",
"page": 1,
"description": ""
}
],
"transaction_status": "processing",
"ref": "TXN-SMS-001",
"product": "sms",
"amount": 3.5,
"total_charge": 8,
"discount": 0,
"trans_id": "5f4c2b1a-9d8e-4f6a-b3c2-1e0d9a8b7c6f",
"created_at": "2026-07-20 11:30:00"
}
},
"status_code": 200
}
Each processed entry's key is spelled recipent (not recipient) — that's the real field name the API returns, not a typo in this doc.
Internet Subscription
List Internet Providers
Retrieve available internet service providers.
Endpoint
GET /internet/list
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Request
- cURL
- Node.js
- Python
- Go
curl -X GET https://sandbox.payscribe.ng/api/v1/internet/list \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/internet/list', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/internet/list',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/internet/list", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Internet service provider fetched successfully",
"message": {
"details": [
{
"id": "spectranet",
"title": "Spectranet",
"active": true
}
]
},
"status_code": 200
}
Spectranet Pin Plans
Retrieve available Spectranet pin plans.
Endpoint
GET /internet/spectranet/pins/plans
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Request
- cURL
- Node.js
- Python
- Go
curl -X GET https://sandbox.payscribe.ng/api/v1/internet/spectranet/pins/plans \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/internet/spectranet/pins/plans', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/internet/spectranet/pins/plans',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/internet/spectranet/pins/plans", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Spectranet pins plans fetched",
"message": {
"details": [
{
"id": "PSPLAN_58",
"title": "Spectranet 10GB Pin",
"amount": 5000
}
]
},
"status_code": 200
}
Use the id value (e.g. PSPLAN_58) as plan_id when calling POST /internet/spectranet/pins/vend.
Purchase Spectranet Pin
Purchase a Spectranet internet pin.
Endpoint
POST /internet/spectranet/pins/vend
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
plan_id | string | Yes | The id value (e.g. PSPLAN_58) from the Spectranet plans list. |
qty | integer | Yes | Number of pins to purchase. |
ref | string | No | Unique reference; only checked for duplicates on the business/API path. |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/internet/spectranet/pins/vend \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"plan_id": "PSPLAN_58", "qty": 1, "ref": "TXN-SPECTRANET-001"}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/internet/spectranet/pins/vend', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"plan_id": "PSPLAN_58",
"qty": 1,
"ref": "TXN-SPECTRANET-001"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"plan_id": "PSPLAN_58",
"qty": 1,
"ref": "TXN-SPECTRANET-001"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/internet/spectranet/pins/vend',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"plan_id": "PSPLAN_58",
"qty": 1,
"ref": "TXN-SPECTRANET-001"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/internet/spectranet/pins/vend", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Order received. Transaction successful.",
"message": {
"details": {
"processed": {
"pin": "89546619",
"serial": "2024103110001386",
"expiry_at": "2028-10-31"
},
"transaction_status": "success",
"product": "spectranet_epins",
"ref": "TXN-SPECTRANET-001",
"amount": 5000,
"total_charge": 4900,
"discount": 100,
"trans_id": "d5e6f7a8-9b0c-4d1e-2f3a-4b5c6d7e8f90",
"created_at": "2026-07-20 11:30:00"
}
},
"status_code": 200
}
Unlike other bill endpoints, spectranetPinsVend has no dedicated sandbox branch — it reuses the same code path for every source, and for a non-api/web source it debits the authenticated dashboard user's personal wallet rather than the business wallet, and skips duplicate-ref checking entirely outside the api/web path. Confirm current behavior with the API team before relying on this response for integration testing.
Validate Spectranet Account
Confirm a Spectranet smartcard or host number before purchasing a pin for it.
This is a live-only endpoint — call it against api.payscribe.ng; there is no sandbox counterpart.
Endpoint
POST /internet/spectranet/validate
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
account | string | Yes | Spectranet smartcard or host number to validate. |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://api.payscribe.ng/api/v1/internet/spectranet/validate \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"account": "1234567890"}'
const response = await fetch('https://api.payscribe.ng/api/v1/internet/spectranet/validate', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({"account": "1234567890"}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"account": "1234567890"
}''')
response = requests.post(
'https://api.payscribe.ng/api/v1/internet/spectranet/validate',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := bytes.NewBufferString(`{"account": "1234567890"}`)
request, err := http.NewRequest(http.MethodPost, "https://api.payscribe.ng/api/v1/internet/spectranet/validate", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": " payment successful.",
"message": {
"details": "{\"status\":\"1\",\"response\":\"Valid subscriber\"}"
},
"status_code": 200
}
message.details is a JSON-encoded string returned by the upstream Spectranet provider — parse it before using the content. The endpoint returns 503 when the service is currently unavailable.
International Bills
List Countries
Retrieve countries supported for international bill payments.
Endpoint
GET /international-bills/countries
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Request
- cURL
- Node.js
- Python
- Go
curl -X GET https://sandbox.payscribe.ng/api/v1/international-bills/countries \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/international-bills/countries', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/international-bills/countries',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/international-bills/countries", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "International bills countries fetched.",
"message": {
"details": [
{
"iso": "GH",
"title": "Ghana",
"prefix": "233"
}
]
},
"status_code": 200
}
List Providers
Retrieve bill payment providers for a country.
Endpoint
GET /international-bills/providers?iso=GH
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
iso | string | Yes | Country ISO code from GET /international-bills/countries (query parameter). |
Request
- cURL
- Node.js
- Python
- Go
curl -X GET "https://sandbox.payscribe.ng/api/v1/international-bills/providers?iso=GH" \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/international-bills/providers?iso=GH', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/international-bills/providers?iso=GH',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/international-bills/providers?iso=GH", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Fetched successfully.",
"message": {
"details": [
{
"code": "MTN",
"name": "MTN",
"phone_regex": "^\\d{10,12}$",
"logo_url": "https://app.payscribe.ng/assets/img/logos/MTN.png"
}
]
},
"status_code": 200
}
List Products
Retrieve bill payment products for a provider.
Endpoint
GET /international-bills/products?iso=GH&code=MTN
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
iso | string | Yes | Country ISO code (query parameter). |
code | string | Yes | Provider code from GET /international-bills/providers (query parameter). |
Request
- cURL
- Node.js
- Python
- Go
curl -X GET "https://sandbox.payscribe.ng/api/v1/international-bills/products?iso=GH&code=MTN" \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/international-bills/products?iso=GH&code=MTN', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/international-bills/products?iso=GH&code=MTN',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/international-bills/products?iso=GH&code=MTN", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Products variation fetched successfully.",
"message": {
"details": [
{
"sku": "NG_MT_TopUp",
"uat": "NG12345",
"country_iso": "GH",
"provider_code": "MTN",
"display_text": "MTN Nigeria TopUp",
"min_send": 1,
"max_send": 50,
"min_receive": 100,
"max_receive": 5000,
"receive_currency": "NGN",
"send_currency": "USD",
"lookup_required": false,
"vend_type": "fixed",
"current_rate": 500
}
]
},
"status_code": 200
}
Use the sku value as variation when calling POST /international-bills/vend.
Check Rate
Get the exchange rate for an international bill payment.
Endpoint
GET /international-bills/rate?iso=NG&sku=NG_MT_TopUp&amount=100
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
iso | string | Yes | Receiving country ISO code (query parameter). |
sku | string | Yes | Product sku from GET /international-bills/products (query parameter). |
amount | integer | Yes | Amount in the receiving currency (query parameter). |
Request
- cURL
- Node.js
- Python
- Go
curl -X GET "https://sandbox.payscribe.ng/api/v1/international-bills/rate?iso=NG&sku=NG_MT_TopUp&amount=100" \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/international-bills/rate?iso=NG&sku=NG_MT_TopUp&amount=100', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/international-bills/rate?iso=NG&sku=NG_MT_TopUp&amount=100',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/international-bills/rate?iso=NG&sku=NG_MT_TopUp&amount=100", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Rate lookup successfully.",
"message": {
"details": {
"ngn_rate": 1500,
"usd_rate": 1.2,
"amount": 100,
"currency": "NG",
"fee": 0
}
},
"status_code": 200
}
Pay International Bill
Make an international bill payment.
Endpoint
POST /international-bills/vend
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Content-Type | application/json |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
ccode | string | Yes | Country ISO code from GET /international-bills/countries. |
phone | string | Yes | Recipient phone number in international format, without +. |
provider | string | Yes | Provider code from GET /international-bills/providers. |
variation | string | Yes | Product sku from GET /international-bills/products. |
amount | number | No | Receive amount; omitted, it defaults to the product's max_receive. |
debit_currency | string | No | Wallet currency to debit. |
ref | string | No | Optional unique reference; used to detect duplicate submissions when provided. |
Request
- cURL
- Node.js
- Python
- Go
curl -X POST https://sandbox.payscribe.ng/api/v1/international-bills/vend \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ccode": "GH",
"phone": "233201234567",
"provider": "GH_MTN",
"variation": "GH_MT_TopUp",
"amount": 10,
"debit_currency": "usd",
"ref": "INTL-REF-001"
}'
const response = await fetch('https://sandbox.payscribe.ng/api/v1/international-bills/vend', {
method: 'POST',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
"ccode": "GH",
"phone": "233201234567",
"provider": "GH_MTN",
"variation": "GH_MT_TopUp",
"amount": 10,
"debit_currency": "usd",
"ref": "INTL-REF-001"
}),
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
import json
payload = json.loads(r'''{
"ccode": "GH",
"phone": "233201234567",
"provider": "GH_MTN",
"variation": "GH_MT_TopUp",
"amount": 10,
"debit_currency": "usd",
"ref": "INTL-REF-001"
}''')
response = requests.post(
'https://sandbox.payscribe.ng/api/v1/international-bills/vend',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}", 'Content-Type': 'application/json'},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"ccode": "GH",
"phone": "233201234567",
"provider": "GH_MTN",
"variation": "GH_MT_TopUp",
"amount": 10,
"debit_currency": "usd",
"ref": "INTL-REF-001"
}`)
request, err := http.NewRequest(http.MethodPost, "https://sandbox.payscribe.ng/api/v1/international-bills/vend", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
request.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Order received. Transaction successful. Usually takes less than 5 min",
"message": {
"details": {
"processed": "233201234567",
"transaction_status": "success",
"amount": 8.33,
"total_charge": 8.33,
"discount": 0,
"trans_id": "e6f7a8b9-0c1d-4e2f-3a4b-5c6d7e8f9a0b",
"datetime": "2026-07-20 11:30:00",
"account": "233201234567",
"country": "GH",
"currency": "GHS",
"sent": "8.33 USD",
"receive": "10 GHS"
}
},
"status_code": 200
}
amount in the response is the debited send-currency amount (USD by default), not the amount you sent in the request — that value is echoed in receive instead.
Requery Transaction
Check the status of a previous transaction.
Endpoint
GET /requery?trans_id=txn_abc123
Headers
| Header | Value |
|---|---|
Authorization | Bearer PAYSCRIBE_API_KEY |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
trans_id | string | Yes | The trans_id or ref from a previous bill payment (query parameter). |
Request
- cURL
- Node.js
- Python
- Go
curl -X GET "https://sandbox.payscribe.ng/api/v1/requery?trans_id=txn_abc123" \
-H "Authorization: Bearer $PAYSCRIBE_API_KEY"
const response = await fetch('https://sandbox.payscribe.ng/api/v1/requery?trans_id=txn_abc123', {
method: 'GET',
headers: {Authorization: `Bearer ${process.env.PAYSCRIBE_API_KEY}`},
});
const result = await response.json().catch(() => null);
if (!response.ok) throw new Error(`Payscribe request failed: ${response.status}`);
console.log(result);
import os
import requests
response = requests.get(
'https://sandbox.payscribe.ng/api/v1/requery?trans_id=txn_abc123',
headers={'Authorization': f"Bearer {os.environ['PAYSCRIBE_API_KEY']}"},
timeout=20,
)
response.raise_for_status()
print(response.json())
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
var body io.Reader
request, err := http.NewRequest(http.MethodGet, "https://sandbox.payscribe.ng/api/v1/requery?trans_id=txn_abc123", body)
if err != nil { panic(err) }
request.Header.Set("Authorization", "Bearer "+os.Getenv("PAYSCRIBE_API_KEY"))
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
if response.StatusCode < 200 || response.StatusCode > 299 { panic(fmt.Sprintf("Payscribe request failed: %s", response.Status)) }
fmt.Println(string(responseBody))
}
Response
Status: 200 OK
{
"status": true,
"description": "Transaction success",
"message": {
"details": {
"trans_id": "txn_abc123",
"ref": "TXN-AIRTIME-001",
"status": "success",
"created_at": "2026-07-20 11:30:00"
}
},
"status_code": 200
}
Fields beyond trans_id, ref, status, and created_at vary by product — any extra data stored on the original transaction (e.g. an electricity token, ePin details) is merged into this response.
Webhooks
| Event | Description |
|---|---|
bills.created | Bill payment request created |
bills.status | Bill payment status updated |
bills.batch.status | Batch bill payment status update |
See Webhooks for payload format and signature verification.
Was this page helpful?