Examples
Complete code examples
These are complete programs you can copy and run, and each one was tested against the real API. They read your details from environment variables, so your secrets stay out of your code.
Setup
| Language | You need |
|---|---|
| PHP | PHP 8 with the curl extension and allow_url_fopen |
| Python | Python 3 and pip install requests |
| Node.js | Node.js 20 or newer. No packages. Save files as .mjs. |
| Environment variable | Used for |
|---|---|
RASTERNIVO_API_KEY | Your API key |
RASTERNIVO_EMAIL, RASTERNIVO_PASSWORD | Creating an account or an API key |
RASTERNIVO_TOKEN | The token from the verification email |
RASTERNIVO_WEBHOOK_SECRET | Your whsec_… webhook secret |
Convert a PDF to JPG
Uploads report.pdf, waits for the job, and saves every page.
<?php
$api = getenv('RASTERNIVO_API_URL') ?: 'https://rasternivo-api.swiftinfocorp.com/v1';
$key = getenv('RASTERNIVO_API_KEY');
function get_json(string $url, string $key): array
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer $key"],
]);
return json_decode(curl_exec($ch), true);
}
// 1. Upload. The options field must come before the file.
$ch = curl_init("$api/conversions");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $key",
'Idempotency-Key: ' . bin2hex(random_bytes(16)),
],
CURLOPT_POSTFIELDS => [
'options' => json_encode(['pages' => 'all', 'dpi' => 150, 'quality' => 90, 'zip' => true]),
'file' => new CURLFile('report.pdf', 'application/pdf'),
],
]);
$job = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_RESPONSE_CODE) !== 202) {
exit('Upload failed: ' . $job['error']['message'] . "\n");
}
// 2. Wait until the job ends.
while (!in_array($job['state'], ['succeeded', 'failed', 'cancelled', 'expired'])) {
sleep(2);
$job = get_json("$api/conversions/{$job['id']}", $key);
}
if ($job['state'] !== 'succeeded') {
exit('Conversion ' . $job['state'] . ': ' . ($job['error']['message'] ?? '') . "\n");
}
// 3. Download every page. The links need no API key and expire after 15 minutes.
$result = get_json("$api/conversions/{$job['id']}/result", $key);
foreach ($result['pages'] as $page) {
if (!empty($page['url'])) {
file_put_contents(basename($page['filename']), file_get_contents($page['url']));
echo 'Saved ', $page['filename'], "\n";
}
}
echo 'Credits used: ', $job['credits_consumed'], "\n";import json
import os
import time
import uuid
import requests
API = os.environ.get("RASTERNIVO_API_URL", "https://rasternivo-api.swiftinfocorp.com/v1")
headers = {"Authorization": "Bearer " + os.environ["RASTERNIVO_API_KEY"]}
# 1. Upload. requests sends the options field before the file, as the API needs.
options = {"pages": "all", "dpi": 150, "quality": 90, "zip": True}
with open("report.pdf", "rb") as pdf:
r = requests.post(
f"{API}/conversions",
headers={**headers, "Idempotency-Key": str(uuid.uuid4())},
data={"options": json.dumps(options)},
files={"file": ("report.pdf", pdf, "application/pdf")},
)
if r.status_code != 202:
raise SystemExit("Upload failed: " + r.json()["error"]["message"])
job = r.json()
# 2. Wait until the job ends.
while job["state"] not in ("succeeded", "failed", "cancelled", "expired"):
time.sleep(2)
job = requests.get(f"{API}/conversions/{job['id']}", headers=headers).json()
if job["state"] != "succeeded":
raise SystemExit(f"Conversion {job['state']}: {job['error']}")
# 3. Download every page. The links need no API key and expire after 15 minutes.
result = requests.get(f"{API}/conversions/{job['id']}/result", headers=headers).json()
for page in result["pages"]:
if page.get("url"):
with open(os.path.basename(page["filename"]), "wb") as out:
out.write(requests.get(page["url"]).content)
print("Saved", page["filename"])
print("Credits used:", job["credits_consumed"])// Node.js 20 or newer: no packages needed.
import { randomUUID } from "node:crypto";
import { readFile, writeFile } from "node:fs/promises";
import { basename } from "node:path";
import { setTimeout as sleep } from "node:timers/promises";
const API = process.env.RASTERNIVO_API_URL ?? "https://rasternivo-api.swiftinfocorp.com/v1";
const headers = { Authorization: `Bearer ${process.env.RASTERNIVO_API_KEY}` };
const getJson = async (path) => (await fetch(API + path, { headers })).json();
// 1. Upload. The options field must come before the file.
const form = new FormData();
form.append("options", JSON.stringify({ pages: "all", dpi: 150, quality: 90, zip: true }));
form.append("file", new Blob([await readFile("report.pdf")], { type: "application/pdf" }), "report.pdf");
const res = await fetch(`${API}/conversions`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": randomUUID() },
body: form,
});
let job = await res.json();
if (res.status !== 202) throw new Error(`Upload failed: ${job.error.message}`);
// 2. Wait until the job ends.
while (!["succeeded", "failed", "cancelled", "expired"].includes(job.state)) {
await sleep(2000);
job = await getJson(`/conversions/${job.id}`);
}
if (job.state !== "succeeded") throw new Error(`Conversion ${job.state}: ${job.error?.message}`);
// 3. Download every page. The links need no API key and expire after 15 minutes.
const result = await getJson(`/conversions/${job.id}/result`);
for (const page of result.pages) {
if (page.url) {
const file = await fetch(page.url);
await writeFile(basename(page.filename), Buffer.from(await file.arrayBuffer()));
console.log("Saved", page.filename);
}
}
console.log("Credits used:", job.credits_consumed);API="https://rasternivo-api.swiftinfocorp.com/v1"
# 1. Upload. The options part must come before the file part.
curl -X POST "$API/conversions" \
-H "Authorization: Bearer $RASTERNIVO_API_KEY" \
-H "Idempotency-Key: report-2026-11-02-001" \
-F 'options={"pages": "all", "dpi": 150, "quality": 90, "zip": true}' \
-F "file=@report.pdf;type=application/pdf"
# 2. Check the job until "state" is succeeded, failed or cancelled.
curl "$API/conversions/JOB_ID" -H "Authorization: Bearer $RASTERNIVO_API_KEY"
# 3. Get the download links, then download.
curl "$API/conversions/JOB_ID/result" -H "Authorization: Bearer $RASTERNIVO_API_KEY"
curl -o report.zip "ZIP_URL_FROM_THE_RESULT"Create an API key
<?php
$api = getenv('RASTERNIVO_API_URL') ?: 'https://rasternivo-api.swiftinfocorp.com/v1';
$email = getenv('RASTERNIVO_EMAIL');
$password = getenv('RASTERNIVO_PASSWORD');
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEFILE => '', // keep the session cookie between the two requests
]);
// 1. Sign in
curl_setopt_array($ch, [
CURLOPT_URL => "$api/auth/login",
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['email' => $email, 'password' => $password]),
]);
$login = json_decode(curl_exec($ch), true);
// 2. Create the key
curl_setopt_array($ch, [
CURLOPT_URL => "$api/api-keys",
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-CSRF-Token: ' . $login['csrf_token']],
CURLOPT_POSTFIELDS => json_encode([
'label' => 'my-server',
'scopes' => ['conversions:read', 'conversions:write', 'usage:read'],
'password' => $password, // asked again because a key is a long-lived credential
]),
]);
$key = json_decode(curl_exec($ch), true);
echo $key['secret'], "\n"; // shown only once: store it safelyimport os
import requests
API = os.environ.get("RASTERNIVO_API_URL", "https://rasternivo-api.swiftinfocorp.com/v1")
email = os.environ["RASTERNIVO_EMAIL"]
password = os.environ["RASTERNIVO_PASSWORD"]
session = requests.Session() # keeps the session cookie between requests
# 1. Sign in
login = session.post(f"{API}/auth/login", json={"email": email, "password": password})
login.raise_for_status()
# 2. Create the key
r = session.post(
f"{API}/api-keys",
headers={"X-CSRF-Token": login.json()["csrf_token"]},
json={
"label": "my-server",
"scopes": ["conversions:read", "conversions:write", "usage:read"],
"password": password, # asked again because a key is a long-lived credential
},
)
r.raise_for_status()
print(r.json()["secret"]) # shown only once: store it safelyconst API = process.env.RASTERNIVO_API_URL ?? "https://rasternivo-api.swiftinfocorp.com/v1";
const email = process.env.RASTERNIVO_EMAIL;
const password = process.env.RASTERNIVO_PASSWORD;
// 1. Sign in
const login = await fetch(`${API}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
if (!login.ok) throw new Error(await login.text());
const { csrf_token } = await login.json();
const cookie = login.headers.getSetCookie().map((c) => c.split(";")[0]).join("; ");
// 2. Create the key
const res = await fetch(`${API}/api-keys`, {
method: "POST",
headers: { "Content-Type": "application/json", "X-CSRF-Token": csrf_token, Cookie: cookie },
body: JSON.stringify({
label: "my-server",
scopes: ["conversions:read", "conversions:write", "usage:read"],
password, // asked again because a key is a long-lived credential
}),
});
if (!res.ok) throw new Error(await res.text());
console.log((await res.json()).secret); // shown only once: store it safelyAPI="https://rasternivo-api.swiftinfocorp.com/v1"
# 1. Sign in. The cookie is saved to session.txt; copy csrf_token from the response.
curl -c session.txt -X POST "$API/auth/login" \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "password": "your-password"}'
# 2. Create the key, sending the cookie, the CSRF token and your password again.
curl -b session.txt -X POST "$API/api-keys" \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: CSRF_TOKEN_FROM_STEP_1" \
-d '{"label": "my-server",
"scopes": ["conversions:read", "conversions:write", "usage:read"],
"password": "your-password"}'Check your plan and credits
<?php
$api = getenv('RASTERNIVO_API_URL') ?: 'https://rasternivo-api.swiftinfocorp.com/v1';
$ch = curl_init("$api/usage");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('RASTERNIVO_API_KEY')],
]);
$usage = json_decode(curl_exec($ch), true);
echo $usage['plan']['name'], ' (', $usage['plan']['source'], '): ',
$usage['credits']['available'], " credits available\n";import os
import requests
API = os.environ.get("RASTERNIVO_API_URL", "https://rasternivo-api.swiftinfocorp.com/v1")
usage = requests.get(
f"{API}/usage",
headers={"Authorization": "Bearer " + os.environ["RASTERNIVO_API_KEY"]},
).json()
print(f"{usage['plan']['name']} ({usage['plan']['source']}): "
f"{usage['credits']['available']} credits available")const API = process.env.RASTERNIVO_API_URL ?? "https://rasternivo-api.swiftinfocorp.com/v1";
const res = await fetch(`${API}/usage`, {
headers: { Authorization: `Bearer ${process.env.RASTERNIVO_API_KEY}` },
});
const usage = await res.json();
console.log(`${usage.plan.name} (${usage.plan.source}): ${usage.credits.available} credits available`);curl "https://rasternivo-api.swiftinfocorp.com/v1/usage" \
-H "Authorization: Bearer $RASTERNIVO_API_KEY"Receive webhooks
A small receiver that checks the signature of every delivery. See Webhooks.
<?php
// webhook.php: point your webhook endpoint at this file.
$secret = getenv('RASTERNIVO_WEBHOOK_SECRET'); // whsec_...
$body = file_get_contents('php://input'); // the raw body, before any JSON parsing
$timestamp = 0;
$signatures = [];
foreach (explode(',', $_SERVER['HTTP_RASTERNIVO_SIGNATURE'] ?? '') as $part) {
[$name, $value] = array_pad(explode('=', $part, 2), 2, '');
if ($name === 't') $timestamp = (int) $value;
if ($name === 'v1') $signatures[] = $value;
}
$expected = hash_hmac('sha256', "$timestamp.$body", $secret);
$valid = false;
foreach ($signatures as $signature) {
$valid = $valid || hash_equals($expected, $signature);
}
if (!$valid || abs(time() - $timestamp) > 300) { // also rejects replays older than 5 minutes
http_response_code(400);
exit;
}
$event = json_decode($body, true);
// De-duplicate with $_SERVER['HTTP_RASTERNIVO_EVENT_ID'], then do your work.
error_log($event['type'] . ' ' . $event['data']['conversion']['id']);
http_response_code(200);# webhook.py: a small receiver using only the standard library.
import hashlib
import hmac
import json
import os
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
SECRET = os.environ["RASTERNIVO_WEBHOOK_SECRET"] # whsec_...
def is_valid(body: bytes, header: str) -> bool:
parts = [p.split("=", 1) for p in header.split(",") if "=" in p]
timestamp = next((v for k, v in parts if k == "t"), "0")
if abs(time.time() - int(timestamp)) > 300: # reject replays older than 5 minutes
return False
expected = hmac.new(SECRET.encode(), f"{timestamp}.".encode() + body, hashlib.sha256).hexdigest()
return any(k == "v1" and hmac.compare_digest(expected, v) for k, v in parts)
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", 0))) # raw body
if not is_valid(body, self.headers.get("RasterNivo-Signature", "")):
self.send_response(400)
self.end_headers()
return
event = json.loads(body)
# De-duplicate with the RasterNivo-Event-Id header, then do your work.
print(event["type"], event["data"]["conversion"]["id"])
self.send_response(200)
self.end_headers()
HTTPServer(("", int(os.environ.get("PORT", 3000))), Handler).serve_forever()// webhook.mjs: a small receiver using only built-in modules.
import { createHmac, timingSafeEqual } from "node:crypto";
import { createServer } from "node:http";
const secret = process.env.RASTERNIVO_WEBHOOK_SECRET; // whsec_...
function isValid(body, header = "") {
const parts = header.split(",").map((p) => p.split("="));
const timestamp = parts.find(([k]) => k === "t")?.[1] ?? "0";
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false; // reject old replays
const expected = Buffer.from(createHmac("sha256", secret).update(`${timestamp}.`).update(body).digest("hex"));
return parts.some(([k, v]) => k === "v1" && v.length === expected.length && timingSafeEqual(Buffer.from(v), expected));
}
createServer((req, res) => {
const chunks = [];
req.on("data", (c) => chunks.push(c));
req.on("end", () => {
const body = Buffer.concat(chunks); // the raw body, before any JSON parsing
if (!isValid(body, req.headers["rasternivo-signature"])) {
res.writeHead(400).end();
return;
}
const event = JSON.parse(body);
// De-duplicate with the rasternivo-event-id header, then do your work.
console.log(event.type, event.data.conversion.id);
res.writeHead(200).end();
});
}).listen(process.env.PORT ?? 3000);Create an account
<?php
$api = getenv('RASTERNIVO_API_URL') ?: 'https://rasternivo-api.swiftinfocorp.com/v1';
$ch = curl_init("$api/auth/register");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'email' => getenv('RASTERNIVO_EMAIL'),
'password' => getenv('RASTERNIVO_PASSWORD'),
'accept_terms' => true,
]),
]);
curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " Check your inbox for the verification email.\n"; // 202import os
import requests
API = os.environ.get("RASTERNIVO_API_URL", "https://rasternivo-api.swiftinfocorp.com/v1")
r = requests.post(f"{API}/auth/register", json={
"email": os.environ["RASTERNIVO_EMAIL"],
"password": os.environ["RASTERNIVO_PASSWORD"],
"accept_terms": True,
})
print(r.status_code, "Check your inbox for the verification email.") # 202const API = process.env.RASTERNIVO_API_URL ?? "https://rasternivo-api.swiftinfocorp.com/v1";
const res = await fetch(`${API}/auth/register`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: process.env.RASTERNIVO_EMAIL,
password: process.env.RASTERNIVO_PASSWORD,
accept_terms: true,
}),
});
console.log(res.status, "Check your inbox for the verification email."); // 202curl -X POST "https://rasternivo-api.swiftinfocorp.com/v1/auth/register" \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "password": "choose-a-long-password", "accept_terms": true}'Verify your email
<?php
$api = getenv('RASTERNIVO_API_URL') ?: 'https://rasternivo-api.swiftinfocorp.com/v1';
$ch = curl_init("$api/auth/verify-email");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['token' => getenv('RASTERNIVO_TOKEN')]),
]);
curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), " Email verified. Your 14-day trial has started.\n";import os
import requests
API = os.environ.get("RASTERNIVO_API_URL", "https://rasternivo-api.swiftinfocorp.com/v1")
r = requests.post(f"{API}/auth/verify-email", json={"token": os.environ["RASTERNIVO_TOKEN"]})
r.raise_for_status()
print("Email verified. Your 14-day trial has started.")const API = process.env.RASTERNIVO_API_URL ?? "https://rasternivo-api.swiftinfocorp.com/v1";
const res = await fetch(`${API}/auth/verify-email`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token: process.env.RASTERNIVO_TOKEN }),
});
if (!res.ok) throw new Error(await res.text());
console.log("Email verified. Your 14-day trial has started.");curl -X POST "https://rasternivo-api.swiftinfocorp.com/v1/auth/verify-email" \
-H "Content-Type: application/json" \
-d '{"token": "THE_TOKEN_FROM_THE_EMAIL"}'