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:
- build the calldata, with
/encodeor by hex-encoding adata:uri yourself. - 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.
- after three blocks, and within ten minutes of the block,
POST /writingswith 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-Zin 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 intok, 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
limno larger thanmax, andmaxmust divide evenly bylim, 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+4E00toU+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-Zlowercase. - 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.