Developer Quickstart
Steps 1 to 4 were run on 2026-10-05 with Node.js 22.22 and @pixagram/dpixa 1.5.4 against api.pixagram.com. Step 5 broadcasts a real vote, so it needs your own account and key; its parts were checked separately (see Sources).
This page takes you from nothing to reading the chain, saving an artwork and signing a vote, in five steps. It assumes you know JavaScript and JSON-RPC, not Hive. If you know Hive, read Differences from Hive next.
Before you start
- Endpoints. Six public API nodes serve the same chain:
https://api.pixagram.com,https://pixarex.net,https://merlion.surf,https://blockforge.lol,https://boitata.questandhttps://pixa-dubai.xyz. All accept browser requests from any origin. - There is no testnet. Every transaction you broadcast is real and stays in the chain forever (Permanence and Provenance). Test with a small account.
- Use the weakest key that works. The posting key votes, posts and follows; the active key moves funds; the owner key is never needed by code (Keys). Pass keys through environment variables. Never write one into a file or a repository. The examples use placeholders such as
5K.... - Requests are limited to about 1 MiB by the public nodes (Chain Parameters).
1. One request with curl
Shellcurl -s -X POST https://api.pixagram.com -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"condenser_api.get_dynamic_global_properties","params":[],"id":1}'
Expected output, shortened; the values change with every block:
JSON{"jsonrpc":"2.0","result":{"head_block_number":901965,"head_block_id":"…","time":"2026-10-05T20:29:48","current_witness":"matus", … ,"current_supply":"100660263.344 PIXA","init_pxs_supply":"0.000 PXS","current_pxs_supply":"251649.961 PXS","total_vesting_fund_pixa":"…", …},"id":1}
The API is Hive's JSON-RPC. Field names that Hive spells with hbd or hive come back with pxs or pixa, because the public nodes rename them (why).
2. Set up a Node.js project
Shellmkdir pixa-quickstart && cd pixa-quickstart
npm init -y
npm install @pixagram/dpixa@1.5.4
Load the bundle, not the package name. In version 1.5.4 the package's entry point names a file that is not published. In Node, require("@pixagram/dpixa") and import { Client } from "@pixagram/dpixa" both fail with Cannot find module …/lib/index-node. Load the bundled build instead:
JavaScript// CommonJS
const { Client, PrivateKey } = require("@pixagram/dpixa/dist/dpixa.js");
// ES modules
import dpixa from "@pixagram/dpixa/dist/dpixa.js";
const { Client, PrivateKey } = dpixa;
Bundlers such as webpack, and browsers loading https://unpkg.com/@pixagram/dpixa@1.5.4/dist/dpixa.js, use the bundle already.
3. Read the chain
Save as read.js:
JavaScript// read.js: three reads through the public API
const { Client } = require("@pixagram/dpixa/dist/dpixa.js");
const client = new Client([
"https://api.pixagram.com",
"https://pixarex.net",
"https://merlion.surf",
]);
async function main() {
const props = await client.database.getDynamicGlobalProperties();
console.log("head block", props.head_block_number, "produced by", props.current_witness);
const [fund] = await client.database.getAccounts(["pixa.omnibus"]);
console.log("pixa.omnibus holds", fund.balance, "and", fund.pxs_balance);
const posts = await client.pixamind.getRankedPosts({ sort: "created", tag: "", limit: 5 });
for (const p of posts) {
const kind = p.json_metadata.format === "image" ? "artwork" : "text post";
console.log(`@${p.author}/${p.permlink}: ${kind}, body ${p.body.length} characters`);
}
}
main().catch((e) => { console.error(e.message); process.exit(1); });
Shellnode read.js
Expected output on 2026-10-05:
head block 901862 produced by top.witness
pixa.omnibus holds 0.000 PIXA and 249295.022 PXS
@mathiew/not-a-bank-robber-1791222997311: artwork, body 25027 characters
@tetiana/schwyz-1791219064927: artwork, body 32547 characters
@retro/peach-knight-1791211388189: artwork, body 23371 characters
@mrdragon/mr-dragon-is-here-1791210562903: artwork, body 11883 characters
@matus/myocastor-coypus-1791149708674: artwork, body 29119 characters
client.databasecalls go to hived;client.pixamindcalls (bridge.*) go to Hivemind (Chain Architecture).pixa.omnibusis the keyless treasury of the Decentralized Pixa Fund.- With several nodes in the list, the client moves to the next one when a node cannot be reached.
4. Save an artwork
An artwork's body is the image itself: a lossless WebP file, base64-encoded as a data URI (Artwork Encoding Spec). Save as artwork.js:
JavaScript// artwork.js: save the newest artwork as a WebP file
const fs = require("fs");
const { Client } = require("@pixagram/dpixa/dist/dpixa.js");
const client = new Client(["https://api.pixagram.com"]);
const PREFIX = "data:image/webp;base64,";
async function main() {
const posts = await client.pixamind.getRankedPosts({ sort: "created", tag: "", limit: 20 });
const art = posts.find((p) => p.json_metadata.format === "image" && p.body.startsWith(PREFIX));
const bytes = Buffer.from(art.body.slice(PREFIX.length), "base64");
// A lossless WebP starts with RIFF, WEBP and VP8L, then packs width − 1 and height − 1 in 14 bits each.
if (bytes.toString("ascii", 0, 4) !== "RIFF" || bytes.toString("ascii", 8, 16) !== "WEBPVP8L") {
throw new Error("not a lossless WebP");
}
const bits = bytes.readUInt32LE(21);
const width = (bits & 0x3fff) + 1;
const height = ((bits >>> 14) & 0x3fff) + 1;
fs.writeFileSync("artwork.webp", bytes);
console.log(`@${art.author}/${art.permlink}: ${width} x ${height} px, ${bytes.length} bytes, saved as artwork.webp`);
}
main().catch((e) => { console.error(e.message); process.exit(1); });
Shellnode artwork.js
Expected output on 2026-10-05:
@mathiew/not-a-bank-robber-1791222997311: 194 x 258 px, 18752 bytes, saved as artwork.webp
Open artwork.webp in any image viewer. It is the original, at its own size; the app only enlarges it on screen.
5. Sign and broadcast a vote
Save as vote.js:
JavaScript// vote.js: sign a vote with your posting key, broadcast it, wait for its block
const { Client, PrivateKey } = require("@pixagram/dpixa/dist/dpixa.js");
const client = new Client(["https://api.pixagram.com"]);
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function main() {
const [voter, author, permlink] = process.argv.slice(2);
const key = PrivateKey.fromString(process.env.POSTING_WIF || ""); // from the environment, never from a file
const { id } = await client.broadcast.vote({ voter, author, permlink, weight: 10000 }, key);
console.log("accepted, transaction", id);
for (let attempt = 0; attempt < 10; attempt++) {
await sleep(3000);
try {
const tx = await client.call("condenser_api", "get_transaction", [id]);
console.log("included in block", tx.block_num);
return;
} catch (e) {
// "Unknown Transaction" until a block holds it
}
}
throw new Error("not in a block after 30 seconds");
}
main().catch((e) => { console.error(e.message); process.exit(1); });
Run it with your account and a post you want to upvote. Read the posting key from the keyboard, so it stays out of your shell history:
Shellread -rsp 'Posting key: ' POSTING_WIF && export POSTING_WIF && echo
node vote.js your-account author permlink
unset POSTING_WIF
Expected output: two lines, the second within a few seconds.
accepted, transaction <40 hexadecimal characters>
included in block <block number>
- What happens. The library reads the head block, builds the transaction, signs it locally and sends it with
condenser_api.broadcast_transaction. Your key never leaves your machine. - What "accepted" means. The broadcast call returns as soon as the node accepts the transaction, with only its
id. It does not wait for a block, so the script askscondenser_api.get_transactionuntil a block holds it. - What the vote does.
weightruns from −10000 (full downvote) to 10000 (full upvote). A full-strength vote spends 2% of your voting mana, and since hardfork 30 it moves rewards from 2.5 Pixa Power; before, it needed more than 2,500 (why).
6. When a call fails
| You see | Cause | What to do |
|---|---|---|
Cannot find module …/lib/index-node |
dpixa 1.5.4's package entry point | Load @pixagram/dpixa/dist/dpixa.js (step 2) |
private key network id mismatch |
POSTING_WIF is empty, or not a private key in WIF form |
Export the key; a WIF starts with 5 |
missing required posting authority … Missing Posting Authority your-account |
The key is not this account's posting key, or the text in your transaction contains a name the public nodes rename (known issue) | Check the key; check the text |
Unable to acquire database lock |
The node was busy | Retry the call |
Could not find API bridge |
A batch request that mixes chain calls with social calls | Send bridge.* calls in their own requests (batches) |
id required |
A batch request whose first call is a social call | Same |
| HTTP 413 | The request is larger than about 1 MiB | Make the artwork smaller |
Invalid asset symbol: PXS |
A Hive library parsing a Pixa amount | Use dpixa (SDKs and Libraries) |
Next steps
- Artwork Encoding Spec: publish an artwork that the Pixagram app displays.
- Differences from Hive: everything that differs from Hive's documentation.
- SDKs and Libraries: dpixa's API, its quirks, and the other
@pixagrampackages. - Run a Node: your own endpoint.
- The Hive developer portal documents every operation and API call. They apply here, with PIXA for HIVE and PXS for HBD.
Sources
- Library:
@pixagram/dpixa1.5.4, source at commitfddb47d; the broadcast call is insrc/helpers/broadcast.ts:312-363. - Checks for step 5, on 2026-10-05: a vote signed by dpixa produced the same digest as the node's own serialization (
condenser_api.get_transaction_hex);condenser_api.get_transactionreturned theblock_numof a known transaction; a wrongly signed vote sent to a local 1.29.0 node returned theMissing Posting Authorityerror above. No transaction was broadcast to the network. - Errors: observed against
api.pixagram.comon 2026-10-05, except the last, from@hiveio/dhive1.3.2.