rbrc

api

everything this site knows, readable and writable over plain http. build your own mint page, a bot, a chart, or a whole competing site on it.

base urlhttps://scan.rbrc20.xyzjson · no key

on this page

base url and limits

the base url is https://scan.rbrc20.xyz. every response is json. there is no key and no signup: reads are open, and the one write there is accepts nothing but a transaction hash, which it verifies against the chain before believing anything about it. you cannot put a lie into the index by asking, only point it at something true.

inscription size
8,192 bytes
confirmations before recording
3 blocks
report window
10 minutes
submissions
20 per minute per ip
list page size
200 rows max

how writing works

the index does not scan blocks. an inscription becomes visible by being reported: you write it on chain yourself, wait three blocks, and post its hash. the server fetches that transaction from the chain and records what it finds there, not what you said. so the flow from any site or script is three steps:

  1. build the calldata, with /encode or by hex-encoding a data: uri yourself.
  2. send it as a transaction to your own address with value 0, signed by your own wallet. this service never signs and never sees a key.
  3. after three blocks, and within ten minutes of the block, POST /writings with the hash.

the ten-minute rule

a writing counts only if it is reported within ten minutes of being written, measured from the timestamp of the block it landed in against the server's clock when the report is checked. a report that arrives later is refused, for good.

the reason is that the rules replay writing in chain order, and without a limit a writing held back and reported late would be replayed ahead of everything written after it. a seller could keep a transfer to themselves, list the same tokens, get paid, and then report the transfer so the buyer's purchase moved nothing. an earlier deploy kept back the same way could take over a ticker other people had already minted. once ten minutes have passed, nothing from before a moment can still be added, so what came before it is settled.

writing recorded before this rule existed has no stored block time. it carries "blockTime": null and is treated as final.

get /encode

turns text into the transaction fields a wallet can sign. anything not already a data: uri is wrapped as one.

curl "https://scan.rbrc20.xyz/encode?text=hello%20chain"

{
  "value": "0x0",
  "data": "0x646174613a2c68656c6c6f20636861696e",
  "text": "data:,hello chain",
  "bytes": 17,
  "note": "send this to your own address with value 0. this service never signs."
}

to is deliberately absent: an inscription is a transaction to yourself, so you fill it with your own address. a service that named an address would be a place for a wrong one to creep in.

post /writings

report an inscription that is already on chain. the body is a hash and nothing else. a hash that is already recorded answers at once with "already": true, even after its ten minutes are up, so retrying a report is always safe.

curl -X POST "https://scan.rbrc20.xyz/writings" \
  -H "content-type: application/json" \
  -d '{"hash":"0xdf88b6091854894f716e8ce7e88e230b14fb971e369c57ed2b1fdb0404393b03"}'

{
  "recorded": true,
  "already": true,
  "writing": {
      "txHash": "0xdf88b6091854894f716e8ce7e88e230b14fb971e369c57ed2b1fdb0404393b03",
      "block": 51544509, "txIndex": 5,
      "from": "0x8648…1cc5", "to": "0x8648…1cc5",
      "media": "text/plain", "protocol": "rbrc",
      "body": "{\"p\":\"rbrc\",\"op\":\"deploy\",\"tick\":\"hood\",\"max\":\"21000000\",\"lim\":\"1000\"}",
      "bytes": 70, "value": "0", "blockTime": null
  }
}

that is a real recorded writing, the deploy of hood. it was recorded before block times were stored, which is why its blockTime is null.

body is what comes after the comma of the data: uri, without the data:, prefix, and bytes counts that body alone. media is the media type from the uri's header, and an empty header reads as text/plain.

a first report answers the same way without already. a refusal is a 422 with a reason in words, and the reasons are exactly these: no transaction with that hash, not in a block yet, block too new (retry in a moment), failed on chain, not a transaction to your own address, carries value, carries no data: uri, larger than 8,192 bytes, contains control bytes, reported too late, or the block's time could not be read (retry in a moment). the one shape exception is a buy: a purchase pays the seller, so a buy inscription must go to another address and must carry value.

the too-late refusal reads, word for word:

{ "error": "that inscription was written more than ten minutes ago. an inscription counts only if it is reported within ten minutes of being written" }

"that transaction is not in a block yet", "that block is too new" and "could not read when that block was made" are worth retrying, and so is "no transaction with that hash" in the first seconds after sending, before the index's node has seen it. the block-time one is a failed read of the chain, not a verdict on the transaction, so ask again well inside the ten minutes. every other refusal, the too-late one included, will not change however often it is asked.

get /writings

everything anyone has written, newest first. filter with address or protocol, page with limit (default 50, max 200) and the before cursor from the previous page's next field.

curl "https://scan.rbrc20.xyz/writings?protocol=rbrc&limit=2"

{
  "count": 2,
  "next": "61900398.0",
  "writings": [ { "txHash": "0x…", "block": 61900412, "txIndex": 1, "from": "0x…",
                    "protocol": "rbrc", "body": "{\"p\":\"rbrc\",\"op\":\"mint\",…}",
                    "blockTime": 1789312345, … }, … ]
}

to replay from the start, ask for order=asc and page forward with the after cursor. each page holds the rows strictly after the cursor, oldest first, and next is again the cursor of the last row on the page.

curl "https://scan.rbrc20.xyz/writings?protocol=rbrc&order=asc&limit=200"
curl "https://scan.rbrc20.xyz/writings?protocol=rbrc&order=asc&limit=200&after=51544509.3"

one writing by hash: GET /writing/0x…. the cursor is block.txIndex rather than an offset, so a page cannot skip or repeat rows while new writing lands on top.

get /tokens

every ticker declared under the rbrc tag. sort=hot (most minted, the default) or sort=new. pass wallet=0x… and each row also carries yourMints and mintsAllowed, so a mint button can know whether it should be pressable before it is painted.

curl "https://scan.rbrc20.xyz/tokens?sort=hot"

{
  "count": 1, "reportWindowSeconds": 600, "now": 1789312900,
  "tokens": [ {
      "tick": "name", "max": "21000000", "lim": "1000",
      "minted": "15000", "percent": 0.07, "soldOut": false,
      "mints": 15, "holders": 3,
      "deployer": "0x…", "block": 61900001, "txHash": "0x…",
      "blockTime": 1789312300, "mintableAt": 1789312900, "settling": false
  } ]
}

blockTime is the deploy's block time, mintableAt is that plus the report window, and settling is true only while now is before mintableAt. both are null, and settling is false, for a deploy recorded before block times were stored. while a token is settling this site does not offer to mint it: an earlier deploy of the same ticker could still be reported, and a mint written now would then belong to that one instead.

get /token/:tick

one ticker, with the same fields except holders: here it is the top 100 holders as a list, not the count /tokens gives. a ticker nobody has declared answers 404 with {"error": "no such ticker"}. a ticker in chinese characters goes in the path percent-encoded as utf-8, once: /token/%E9%BE%99.

curl "https://scan.rbrc20.xyz/token/name"

{
  "tick": "name", "max": "21000000", "lim": "1000", "minted": "15000",
  "percent": 0.07, "soldOut": false, "mints": 15,
  "deployer": "0x…", "block": 61900001, "txHash": "0x…",
  "blockTime": 1789312300, "mintableAt": 1789312900, "settling": false,
  "holders": [ { "address": "0x…", "amount": "5000" }, … ]
}

get /market

open listings, grouped by ticker, cheapest per unit first, each group with its own floor. ?tick=name filters to one ticker; ?flat=1 returns one flat listings list instead of groups.

curl "https://scan.rbrc20.xyz/market?tick=name"

{
  "tickers": 1, "listings": 3, "reportWindowSeconds": 600, "now": 1789312900,
  "groups": [ {
      "tick": "name", "floor": "1800000000000", "listings": 3, "forSale": "4000",
      "minted": "15000", "holders": 3,
      "rows": [ { "id": "0x…", "amt": "1000", "price": "1800000000000000",
                  "unit": "1800000000000", "seller": "0x…", "block": 61900012,
                  "blockTime": 1789312500, "buyableAt": 1789313100, "settling": true }, … ]
  } ]
}

prices are wei. id is the hash of the listing inscription, and it is what a buy names. buyableAt and settling work like the token fields: while a listing is settling, the seller can still report a transfer written before it that empties the balance behind it, so this site does not offer to buy it until buyableAt.

get /balances/:addr

what one address holds, across every ticker, with how much of it is committed to open listings and how much of the mint allowance is used.

curl "https://scan.rbrc20.xyz/balances/0xyouraddress"

{
  "address": "0xyouraddress",
  "balances": [ { "tick": "name", "amount": "5000", "listed": "1000", "free": "4000",
                    "lim": "1000", "max": "21000000", "mintsUsed": 5, "mintsAllowed": 20 } ]
}

get /activity

the event feed, newest first: every deploy, mint, transfer, list, cancel and buy the rules accepted. filter with tick or address, cap with limit (max 200).

curl "https://scan.rbrc20.xyz/activity?tick=name&limit=1"

{
  "events": [ { "kind": "buy", "tick": "name", "who": "0x…", "seller": "0x…",
                  "amt": "1000", "price": "1800000000000000", "block": 61900031, "txHash": "0x…" } ]
}

get /health

whether the index can see the chain, and how much it has recorded.

curl "https://scan.rbrc20.xyz/health"

{
  "ok": true, "protocol": "rbrc", "head": 61900950, "rpcOk": true,
  "writings": 74, "lastWritingBlock": 61900412,
  "reportWindowSeconds": 600, "rulesV1Block": 61834151, "rulesV2Block": null
}

ok is false when the chain head cannot be read. while it is, new reports cannot be checked, so a writing's ten minutes can run out; reads of what is already recorded keep working. rulesV2Block is the block rules v2 start from, or null while v2 is not active, which is the case in production today. an index that predates v2 leaves the field out; read that as null too.

the operations

the token rules live in the reader, not in a contract. an operation is a compact json object in a data: uri, protocol tag rbrc, written as a transaction to your own address with value 0, except a buy, which is written on the payment itself. amounts are strings.

deploy    data:,{"p":"rbrc","op":"deploy","tick":"name","max":"21000000","lim":"1000"}
mint      data:,{"p":"rbrc","op":"mint","tick":"name","amt":"1000"}
transfer  data:,{"p":"rbrc","op":"transfer","tick":"name","amt":"1000","to":"0x…"}
list      data:,{"p":"rbrc","op":"list","tick":"name","amt":"1000","price":"1800000000000000"}
cancel    data:,{"p":"rbrc","op":"cancel","id":"0x…listing tx hash…"}
buy       data:,{"p":"rbrc","op":"buy","id":"0x…listing tx hash…"}   (sent to the seller, value >= price)

the rules a writing has to satisfy to count, in full: a ticker is 1 to 16 characters of a-z, 0-9 and - (from block 61,834,151 the ascii letters A-Z are lowercased first, and nothing else is), and a writing naming any other ticker is ignored. rules v2, below, add chinese tickers once active. the first deploy of a ticker wins and every later one is ignored. a mint must be exactly lim, is refused past max, and a wallet gets at most 20 mints per ticker. transfers and listings move whole mint units only, and only balance not already committed to an open listing. a cancel must come from the seller. it takes the listing off the market at once, but from rules v1 the amount stays locked for ten minutes after the cancel (twenty for a cancel from rules v2 on), and a buy mined in that time still completes: whoever sent it could not have seen the cancel yet. a buy counts only if the payment actually went to the seller and actually covered the price, both read from the chain, so nothing can be taken by asking nicely.

rules v1, from block 61,834,151

writing in block 61,834,151 or later is also held to these. writing before that block is read exactly as it always was, so no balance that already existed changes.

  • the ascii letters A-Z in a ticker are lowercased before it is checked, so a ticker written in capitals is the same ticker as the one written in lowercase. no other character is changed: a unicode lowercase would turn the kelvin sign into k, and the index does not do that.
  • every amount (max, lim, amt, price) must be a canonical positive whole number: digits only, no leading zero, not zero, at most 30 digits.
  • a deploy needs lim no larger than max, and max must divide evenly by lim, so a supply can always be minted to the last unit.
  • a listing needs a price above zero.
  • a cancel keeps the listing's amount locked for ten minutes after the cancel's block, and a buy mined in that time still fills the listing.
  • the protocol tag is matched without regard to case when a buy is read.

a writing that breaks a rule is not an error anywhere: it is recorded as writing and simply does nothing to any balance.

rules v2: chinese tickers

rules v2 are written and switched off. they apply from the block in rulesV2Block on /health, which is null in production today, so nothing below counts yet. the block is chosen before it is produced, and once set it never moves. writing before it keeps its v1 meaning for good: a chinese deploy written earlier is ignored and reserves nothing.

  • a ticker is either the v1 ascii form, or 1 to 8 characters each in U+4E00 to U+9FA5, the cjk unified ideographs block as unicode 1.1 defined it. nothing else, and never the two mixed.
  • the chinese form is used exactly as written: no trimming, no case mapping, no unicode normalization, no folding of variants or look-alikes. the only change to any ticker is the ascii A-Z lowercase.
  • two tickers are the same only when their characters are the same code points. a simplified and a traditional form, such as and , are two different tickers, each with its own first deploy.
  • the ticker is read after json parsing, so "\u9f99" and "龙" name the same ticker. write the character itself.
  • amounts, deploy limits, listing prices, the report window and the per-wallet cap do not change. the one change is the cancel lock: a cancel mined from this block on keeps the amount locked, and still honours buys, for twenty minutes, two report windows.

this site offers a chinese deploy or mint only once the index's head has reached rulesV2Block, and warns when a ticker differs from a declared one only by a simplified or traditional form or a look-alike character. the warning is advice; the rules decide nothing with it.

when two buys land for one listing

a listing can be bought once. if two buys for the same listing land, the later payment still reaches the seller, because it is a plain transfer of eth that no rule can stop, and it moves no tokens. the same goes for a buy that lands after a cancel's lock has ended: ten minutes after the cancel, or twenty for a cancel from rules v2 on. there is no refund mechanism. so check that the listing is still open right before paying, and do not send a buy for a listing someone else may be buying at the same moment.

a purchase can still be voided for up to ten minutes after it lands. "first" is chain order, not the order reports arrive in, so an earlier buy of the same listing that had not been reported yet can be reported inside its own ten minutes and take the listing. your buy then moves no tokens, your eth has still reached the seller, and there is no refund.

checking the index

every recorded writing names its transaction, so each one can be checked on chain: the sender, the recipient, the value and the exact calldata are all there to compare. and GET /writings?order=asc hands anyone the whole recorded set in chain order, so anyone can rerun the rules over it and see whether they reach the numbers this api serves.

what the chain alone cannot tell you is which writings were reported in time. that list is the index's record, which is why each row keeps its transaction and its block time, so the part you are trusting is as small and as checkable as it can be.

back to the top