chem_formula
Parse a chemical formula with parentheses, brackets, hydrates (CuSO4·5H2O or CuSO4*5H2O), leading coefficients and ionic charges, and return the element counts, the Hill formula, the molar mass and the mass percent of each element, summed exactly in decimal from the bundled IUPAC/CIAAW abridged standard atomic weights 2024. Accepts Unicode subscripts and superscripts and several formulas at once.
Install
/tools install chem_formula /tools install chem_formula@1.0.0
In a console session, or let the model find it in normal mode: axlr_catalog describes it and axlr_install_tool asks for the same install card. The card reads catalog axlr · chem_formula 1.0.0 · verified once the index signature, the manifest digest and every file digest check out. Then the model runs it with axlr_run_tool chem_formula; each run is approved as a local_exec.
Arguments
One JSON object on stdin, validated against the schema before the program starts.
| name | type | required | default | meaning |
|---|---|---|---|---|
formula | string | no | Ca(OH)2, K4[Fe(CN)6], CuSO4·5H2O, SO4^2-, NH4+. length ≤ 500 | |
formulas | array of string | no | items ≥ 1; items ≤ 100 | |
decimals | integer | no | 2 | Digits of molar_mass_rounded and mass_percent. ≥ 0; ≤ 6 |
masses | object of ['number', 'string'] | no | Atomic weights for elements without a standard atomic weight (Tc, Pm, Po...) or overrides, by symbol |
Input schema (JSON)
{
"type": "object",
"additionalProperties": false,
"properties": {
"formula": {
"type": "string",
"maxLength": 500,
"description": "Ca(OH)2, K4[Fe(CN)6], CuSO4·5H2O, SO4^2-, NH4+"
},
"formulas": {
"type": "array",
"items": {
"type": "string",
"maxLength": 500
},
"minItems": 1,
"maxItems": 100
},
"decimals": {
"type": "integer",
"minimum": 0,
"maximum": 6,
"default": 2,
"description": "Digits of molar_mass_rounded and mass_percent"
},
"masses": {
"type": "object",
"additionalProperties": {
"type": [
"number",
"string"
]
},
"description": "Atomic weights for elements without a standard atomic weight (Tc, Pm, Po...) or overrides, by symbol"
}
}
}
Example
Input
{
"formula": "CuSO4·5H2O"
}
Output (stdout)
{
"hill": "CuH10O9S",
"counts": {
"Cu": 1,
"S": 1,
"O": 9,
"H": 10
},
"molar_mass": "249.6770",
"molar_mass_rounded": "249.68",
"unit": "g/mol"
}
Files
The command the install card registers is python3 chem_formula.py, run in the workspace with PATH and HOME as its only environment. Each file's SHA-256 is in the signed index; the console checks them before showing the card and again before writing.
chem_formula.py · sha256 752e6b4030954562f4048a4f3574e3c0628649378fb45fe12fc7cad0d81b23bf
#!/usr/bin/env python3
"""Parse a chemical formula and compute element counts, molar mass and mass percent from the
bundled IUPAC/CIAAW abridged standard atomic weights (elements.json, written by
tools/catalog/data/chem_formula.py).
Input (JSON on stdin):
formula "Ca(OH)2", "K4[Fe(CN)6]", "CuSO4·5H2O" (also . * • as the hydrate dot),
"SO4^2-", "NH4+", "Fe+++", "SO4 2-", "2H2O" (leading coefficient)
formulas a list of formulas instead of one (at most 100)
decimals digits of molar_mass_rounded and mass_percent, default 2 (0-6)
masses {"Tc": 98} atomic weights for elements without a standard atomic weight, or to override one
"""
import json
import pathlib
import re
import sys
from decimal import ROUND_HALF_UP, Decimal, InvalidOperation
TABLE = pathlib.Path(__file__).resolve().parent / "elements.json"
DOTS = "·•∙⋅.*"
SUB = str.maketrans("₀₁₂₃₄₅₆₇₈₉", "0123456789")
SUP = str.maketrans("⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻", "0123456789+-")
SUP_CHARS = "⁰¹²³⁴⁵⁶⁷⁸⁹"
CLOSE = {"(": ")", "[": "]", "{": "}"}
class FormulaError(Exception):
pass
def fail(message):
print(json.dumps({"error": message}, ensure_ascii=False))
sys.exit(2)
def split_charge(text):
"""Return (formula without charge, charge as int)."""
patterns = [
(r"\^\{?(\d*)([+-])\}?$", lambda m: (m.group(1), m.group(2))),
(r"\^\{?([+-])(\d*)\}?$", lambda m: (m.group(2), m.group(1))),
(r"\s+(\d*)([+-])$", lambda m: (m.group(1), m.group(2))),
]
for pattern, parts in patterns:
m = re.search(pattern, text)
if m:
digits, sign = parts(m)
size = int(digits) if digits else 1
return text[:m.start()], size if sign == "+" else -size
m = re.search(f"([{SUP_CHARS}]*)([⁺⁻])$", text)
if m:
digits = m.group(1).translate(SUP)
size = int(digits) if digits else 1
return text[:m.start()], size if m.group(2) == "⁺" else -size
m = re.search(r"(\++|-+)$", text)
if m:
size = len(m.group(1))
return text[:m.start()], size if m.group(1)[0] == "+" else -size
return text, 0
class Parser:
def __init__(self, text, known):
self.text, self.pos, self.known = text, 0, known
def peek(self):
return self.text[self.pos] if self.pos < len(self.text) else ""
def number(self):
start = self.pos
while self.peek().isdigit():
self.pos += 1
return int(self.text[start:self.pos]) if self.pos > start else None
def group(self, closing=None):
counts = {}
while True:
ch = self.peek()
if ch == "":
if closing:
raise FormulaError(f"missing '{closing}'")
break
if ch == closing:
break
if ch in CLOSE:
self.pos += 1
inner = self.group(CLOSE[ch])
if self.peek() != CLOSE[ch]:
raise FormulaError(f"missing '{CLOSE[ch]}'")
self.pos += 1
n = self.number()
if n == 0:
raise FormulaError(f"zero count after '{CLOSE[ch]}' at position {self.pos}")
for symbol, c in inner.items():
counts[symbol] = counts.get(symbol, 0) + c * (n or 1)
elif ch.isupper():
start = self.pos
self.pos += 1
if self.peek().islower():
self.pos += 1
symbol = self.text[start:self.pos]
if symbol not in self.known:
raise FormulaError(f"unknown element '{symbol}' at position {start + 1}")
n = self.number()
if n == 0:
raise FormulaError(f"zero count after {symbol}")
counts[symbol] = counts.get(symbol, 0) + (n or 1)
elif ch in ")]}":
raise FormulaError(f"unexpected '{ch}' at position {self.pos + 1}")
elif ch.islower():
raise FormulaError(f"element symbols start with a capital letter: '{ch}' at position {self.pos + 1}")
elif ch.isdigit():
raise FormulaError(f"a count must follow an element or a bracket (position {self.pos + 1})")
else:
raise FormulaError(f"unexpected character '{ch}' at position {self.pos + 1}")
return counts
def parse(formula, known):
text = formula.strip().translate(SUB)
if not text:
raise FormulaError("empty formula")
text, charge = split_charge(text)
text = text.replace(" ", "")
pieces = re.split(f"[{re.escape(DOTS)}]", text)
total, parts = {}, []
for piece in pieces:
if not piece:
raise FormulaError("empty part around a hydrate dot")
m = re.match(r"\d+", piece)
coefficient = int(m.group()) if m else 1
body = piece[m.end():] if m else piece
if coefficient == 0 or not body:
raise FormulaError(f"bad part '{piece}'")
parser = Parser(body, known)
counts = parser.group()
if parser.pos != len(body):
raise FormulaError(f"unexpected '{body[parser.pos]}'")
parts.append({"part": piece, "coefficient": coefficient})
for symbol, c in counts.items():
total[symbol] = total.get(symbol, 0) + c * coefficient
return total, charge, parts
def hill(counts, charge):
symbols = sorted(counts)
if "C" in counts:
symbols = ["C"] + (["H"] if "H" in counts else []) + [s for s in symbols if s not in ("C", "H")]
text = "".join(s + (str(counts[s]) if counts[s] > 1 else "") for s in symbols)
if charge:
text += f"^{abs(charge) if abs(charge) > 1 else ''}{'+' if charge > 0 else '-'}"
return text, symbols
def compute(formula, table, overrides, decimals):
known = {e["symbol"]: e for e in table}
counts, charge, parts = parse(formula, known)
text, order = hill(counts, charge)
q = Decimal(1).scaleb(-decimals)
total, unc, rows = Decimal(0), Decimal(0), []
for symbol in order:
e = known[symbol]
if symbol in overrides:
weight, u, origin = overrides[symbol], None, "input"
elif e["weight"] is None:
raise FormulaError(f"{e['name']} ({symbol}) has no standard atomic weight; give it in masses")
else:
weight, u, origin = Decimal(e["weight"]), Decimal(e["uncertainty"]), "CIAAW 2024"
mass = weight * counts[symbol]
total += mass
if u is not None:
unc += u * counts[symbol]
rows.append({"symbol": symbol, "name": e["name"], "count": counts[symbol], "atomic_weight": str(weight),
"weight_source": origin, "mass": str(mass)})
for row in rows:
row["mass_percent"] = str((Decimal(row["mass"]) * 100 / total).quantize(q, ROUND_HALF_UP))
out = {"formula": formula, "hill": text, "charge": charge, "parts": parts,
"counts": counts, "atoms": sum(counts.values()), "elements": rows,
"molar_mass": str(total), "molar_mass_rounded": str(total.quantize(q, ROUND_HALF_UP)), "unit": "g/mol",
"abridged_uncertainty_sum": str(unc)}
if charge:
out["note"] = "molar mass of the ion ignores the mass of the electrons gained or lost"
return out
def main():
try:
request = json.load(sys.stdin)
except ValueError as error:
fail(f"stdin is not JSON: {error}")
if not isinstance(request, dict):
fail("input must be a JSON object")
extra = set(request) - {"formula", "formulas", "decimals", "masses"}
if extra:
fail(f"unknown field(s): {', '.join(sorted(extra))}")
try:
data = json.loads(TABLE.read_text(encoding="utf-8"))
except (OSError, ValueError) as error:
fail(f"cannot read the bundled table: {error}")
table = data["elements"]
decimals = request.get("decimals", 2)
if isinstance(decimals, bool) or not isinstance(decimals, int) or not 0 <= decimals <= 6:
fail("decimals must be an integer from 0 to 6")
overrides = {}
masses = request.get("masses", {})
if not isinstance(masses, dict):
fail("masses must be an object of element symbol to atomic weight")
symbols = {e["symbol"] for e in table}
for symbol, value in masses.items():
if symbol not in symbols:
fail(f"masses: unknown element '{symbol}'")
if isinstance(value, bool) or not isinstance(value, (int, float, str)):
fail(f"masses.{symbol} must be a number")
try:
weight = Decimal(str(value).strip())
except InvalidOperation:
fail(f"masses.{symbol} must be a number")
if not weight.is_finite() or weight <= 0:
fail(f"masses.{symbol} must be positive")
overrides[symbol] = weight
source = {"table": data["title"], "basis": data["basis"], "url": data["source_url"], "fetched": data["fetched"]}
if ("formula" in request) == ("formulas" in request):
fail("give formula or formulas")
if "formula" in request:
formula = request["formula"]
if not isinstance(formula, str) or len(formula) > 500:
fail("formula must be a string of at most 500 characters")
try:
result = compute(formula, table, overrides, decimals)
except FormulaError as error:
fail(f"formula {formula!r}: {error}")
result["source"] = source
print(json.dumps(result, ensure_ascii=False))
return
formulas = request["formulas"]
if not isinstance(formulas, list) or not 1 <= len(formulas) <= 100:
fail("formulas must be a list of 1 to 100 strings")
results = []
for i, formula in enumerate(formulas):
if not isinstance(formula, str) or len(formula) > 500:
fail(f"formulas[{i}] must be a string of at most 500 characters")
try:
results.append(compute(formula, table, overrides, decimals))
except FormulaError as error:
results.append({"formula": formula, "error": str(error)})
print(json.dumps({"results": results, "source": source}, ensure_ascii=False))
if __name__ == "__main__":
try:
main()
except SystemExit:
raise
except Exception as error: # never a traceback
fail(f"{type(error).__name__}: {error}")
elements.json · sha256 c1ac4b63fb2f6a9131930e23fbd6a11b5217a23b49106ec4483e1984ffb58cb9
{
"title": "IUPAC/CIAAW Abridged Standard Atomic Weights 2024",
"basis": "Atomic Weights 2021 report (Prohaska et al., Pure Appl. Chem. 94 (2022) 573-600) with the 2024 revisions of Gd, Lu and Zr",
"source_url": "https://www.ciaaw.org/abridged-atomic-weights.htm",
"licence": "official table of the IUPAC commission (page (c) CIAAW); numerical values reproduced as facts, with attribution",
"fetched": "2026-10-18",
"generator": "tools/catalog/data/chem_formula.py",
"notes_legend": {
"g": "geological materials with an isotopic composition outside normal limits are known",
"m": "modified isotopic compositions may be found in commercial material",
"r": "range in isotopic composition of normal terrestrial material limits precision"
},
"elements": [
{
"z": 1,
"symbol": "H",
"name": "hydrogen",
"weight": "1.0080",
"uncertainty": "0.0002",
"notes": [
"m"
]
},
{
"z": 2,
"symbol": "He",
"name": "helium",
"weight": "4.0026",
"uncertainty": "0.0001",
"notes": [
"g",
"r"
]
},
{
"z": 3,
"symbol": "Li",
"name": "lithium",
"weight": "6.94",
"uncertainty": "0.06",
"notes": [
"m"
]
},
{
"z": 4,
"symbol": "Be",
"name": "beryllium",
"weight": "9.0122",
"uncertainty": "0.0001",
"notes": []
},
{
"z": 5,
"symbol": "B",
"name": "boron",
"weight": "10.81",
"uncertainty": "0.02",
"notes": [
"m"
]
},
{
"z": 6,
"symbol": "C",
"name": "carbon",
"weight": "12.011",
"uncertainty": "0.002",
"notes": []
},
{
"z": 7,
"symbol": "N",
"name": "nitrogen",
"weight": "14.007",
"uncertainty": "0.001",
"notes": [
"m"
]
},
{
"z": 8,
"symbol": "O",
"name": "oxygen",
"weight": "15.999",
"uncertainty": "0.001",
"notes": [
"m"
]
},
{
"z": 9,
"symbol": "F",
"name": "fluorine",
"weight": "18.998",
"uncertainty": "0.001",
"notes": []
},
{
"z": 10,
"symbol": "Ne",
"name": "neon",
"weight": "20.180",
"uncertainty": "0.001",
"notes": [
"g",
"m"
]
},
{
"z": 11,
"symbol": "Na",
"name": "sodium",
"weight": "22.990",
"uncertainty": "0.001",
"notes": []
},
{
"z": 12,
"symbol": "Mg",
"name": "magnesium",
"weight": "24.305",
"uncertainty": "0.002",
"notes": []
},
{
"z": 13,
"symbol": "Al",
"name": "aluminium",
"weight": "26.982",
"uncertainty": "0.001",
"notes": []
},
{
"z": 14,
"symbol": "Si",
"name": "silicon",
"weight": "28.085",
"uncertainty": "0.001",
"notes": []
},
{
"z": 15,
"symbol": "P",
"name": "phosphorus",
"weight": "30.974",
"uncertainty": "0.001",
"notes": []
},
{
"z": 16,
"symbol": "S",
"name": "sulfur",
"weight": "32.06",
"uncertainty": "0.02",
"notes": []
},
{
"z": 17,
"symbol": "Cl",
"name": "chlorine",
"weight": "35.45",
"uncertainty": "0.01",
"notes": [
"m"
]
},
{
"z": 18,
"symbol": "Ar",
"name": "argon",
"weight": "39.95",
"uncertainty": "0.16",
"notes": []
},
{
"z": 19,
"symbol": "K",
"name": "potassium",
"weight": "39.098",
"uncertainty": "0.001",
"notes": []
},
{
"z": 20,
"symbol": "Ca",
"name": "calcium",
"weight": "40.078",
"uncertainty": "0.004",
"notes": [
"g"
]
},
{
"z": 21,
"symbol": "Sc",
"name": "scandium",
"weight": "44.956",
"uncertainty": "0.001",
"notes": []
},
{
"z": 22,
"symbol": "Ti",
"name": "titanium",
"weight": "47.867",
"uncertainty": "0.001",
"notes": []
},
{
"z": 23,
"symbol": "V",
"name": "vanadium",
"weight": "50.942",
"uncertainty": "0.001",
"notes": []
},
{
"z": 24,
"symbol": "Cr",
"name": "chromium",
"weight": "51.996",
"uncertainty": "0.001",
"notes": []
},
{
"z": 25,
"symbol": "Mn",
"name": "manganese",
"weight": "54.938",
"uncertainty": "0.001",
"notes": []
},
{
"z": 26,
"symbol": "Fe",
"name": "iron",
"weight": "55.845",
"uncertainty": "0.002",
"notes": []
},
{
"z": 27,
"symbol": "Co",
"name": "cobalt",
"weight": "58.933",
"uncertainty": "0.001",
"notes": []
},
{
"z": 28,
"symbol": "Ni",
"name": "nickel",
"weight": "58.693",
"uncertainty": "0.001",
"notes": [
"r"
]
},
{
"z": 29,
"symbol": "Cu",
"name": "copper",
"weight": "63.546",
"uncertainty": "0.003",
"notes": [
"r"
]
},
{
"z": 30,
"symbol": "Zn",
"name": "zinc",
"weight": "65.38",
"uncertainty": "0.02",
"notes": [
"r"
]
},
{
"z": 31,
"symbol": "Ga",
"name": "gallium",
"weight": "69.723",
"uncertainty": "0.001",
"notes": []
},
{
"z": 32,
"symbol": "Ge",
"name": "germanium",
"weight": "72.630",
"uncertainty": "0.008",
"notes": []
},
{
"z": 33,
"symbol": "As",
"name": "arsenic",
"weight": "74.922",
"uncertainty": "0.001",
"notes": []
},
{
"z": 34,
"symbol": "Se",
"name": "selenium",
"weight": "78.971",
"uncertainty": "0.008",
"notes": [
"r"
]
},
{
"z": 35,
"symbol": "Br",
"name": "bromine",
"weight": "79.904",
"uncertainty": "0.003",
"notes": []
},
{
"z": 36,
"symbol": "Kr",
"name": "krypton",
"weight": "83.798",
"uncertainty": "0.002",
"notes": [
"g",
"m"
]
},
{
"z": 37,
"symbol": "Rb",
"name": "rubidium",
"weight": "85.468",
"uncertainty": "0.001",
"notes": [
"g"
]
},
{
"z": 38,
"symbol": "Sr",
"name": "strontium",
"weight": "87.62",
"uncertainty": "0.01",
"notes": [
"g",
"r"
]
},
{
"z": 39,
"symbol": "Y",
"name": "yttrium",
"weight": "88.906",
"uncertainty": "0.001",
"notes": []
},
{
"z": 40,
"symbol": "Zr",
"name": "zirconium",
"weight": "91.222",
"uncertainty": "0.003",
"notes": [
"g"
]
},
{
"z": 41,
"symbol": "Nb",
"name": "niobium",
"weight": "92.906",
"uncertainty": "0.001",
"notes": []
},
{
"z": 42,
"symbol": "Mo",
"name": "molybdenum",
"weight": "95.95",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 43,
"symbol": "Tc",
"name": "technetium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 44,
"symbol": "Ru",
"name": "ruthenium",
"weight": "101.07",
"uncertainty": "0.02",
"notes": [
"g"
]
},
{
"z": 45,
"symbol": "Rh",
"name": "rhodium",
"weight": "102.91",
"uncertainty": "0.01",
"notes": []
},
{
"z": 46,
"symbol": "Pd",
"name": "palladium",
"weight": "106.42",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 47,
"symbol": "Ag",
"name": "silver",
"weight": "107.87",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 48,
"symbol": "Cd",
"name": "cadmium",
"weight": "112.41",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 49,
"symbol": "In",
"name": "indium",
"weight": "114.82",
"uncertainty": "0.01",
"notes": []
},
{
"z": 50,
"symbol": "Sn",
"name": "tin",
"weight": "118.71",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 51,
"symbol": "Sb",
"name": "antimony",
"weight": "121.76",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 52,
"symbol": "Te",
"name": "tellurium",
"weight": "127.60",
"uncertainty": "0.03",
"notes": [
"g"
]
},
{
"z": 53,
"symbol": "I",
"name": "iodine",
"weight": "126.90",
"uncertainty": "0.01",
"notes": []
},
{
"z": 54,
"symbol": "Xe",
"name": "xenon",
"weight": "131.29",
"uncertainty": "0.01",
"notes": [
"g",
"m"
]
},
{
"z": 55,
"symbol": "Cs",
"name": "caesium",
"weight": "132.91",
"uncertainty": "0.01",
"notes": []
},
{
"z": 56,
"symbol": "Ba",
"name": "barium",
"weight": "137.33",
"uncertainty": "0.01",
"notes": []
},
{
"z": 57,
"symbol": "La",
"name": "lanthanum",
"weight": "138.91",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 58,
"symbol": "Ce",
"name": "cerium",
"weight": "140.12",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 59,
"symbol": "Pr",
"name": "praseodymium",
"weight": "140.91",
"uncertainty": "0.01",
"notes": []
},
{
"z": 60,
"symbol": "Nd",
"name": "neodymium",
"weight": "144.24",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 61,
"symbol": "Pm",
"name": "promethium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 62,
"symbol": "Sm",
"name": "samarium",
"weight": "150.36",
"uncertainty": "0.02",
"notes": [
"g"
]
},
{
"z": 63,
"symbol": "Eu",
"name": "europium",
"weight": "151.96",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 64,
"symbol": "Gd",
"name": "gadolinium",
"weight": "157.25",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 65,
"symbol": "Tb",
"name": "terbium",
"weight": "158.93",
"uncertainty": "0.01",
"notes": []
},
{
"z": 66,
"symbol": "Dy",
"name": "dysprosium",
"weight": "162.50",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 67,
"symbol": "Ho",
"name": "holmium",
"weight": "164.93",
"uncertainty": "0.01",
"notes": []
},
{
"z": 68,
"symbol": "Er",
"name": "erbium",
"weight": "167.26",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 69,
"symbol": "Tm",
"name": "thulium",
"weight": "168.93",
"uncertainty": "0.01",
"notes": []
},
{
"z": 70,
"symbol": "Yb",
"name": "ytterbium",
"weight": "173.05",
"uncertainty": "0.02",
"notes": [
"g"
]
},
{
"z": 71,
"symbol": "Lu",
"name": "lutetium",
"weight": "174.97",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 72,
"symbol": "Hf",
"name": "hafnium",
"weight": "178.49",
"uncertainty": "0.01",
"notes": []
},
{
"z": 73,
"symbol": "Ta",
"name": "tantalum",
"weight": "180.95",
"uncertainty": "0.01",
"notes": []
},
{
"z": 74,
"symbol": "W",
"name": "tungsten",
"weight": "183.84",
"uncertainty": "0.01",
"notes": []
},
{
"z": 75,
"symbol": "Re",
"name": "rhenium",
"weight": "186.21",
"uncertainty": "0.01",
"notes": []
},
{
"z": 76,
"symbol": "Os",
"name": "osmium",
"weight": "190.23",
"uncertainty": "0.03",
"notes": [
"g"
]
},
{
"z": 77,
"symbol": "Ir",
"name": "iridium",
"weight": "192.22",
"uncertainty": "0.01",
"notes": []
},
{
"z": 78,
"symbol": "Pt",
"name": "platinum",
"weight": "195.08",
"uncertainty": "0.02",
"notes": []
},
{
"z": 79,
"symbol": "Au",
"name": "gold",
"weight": "196.97",
"uncertainty": "0.01",
"notes": []
},
{
"z": 80,
"symbol": "Hg",
"name": "mercury",
"weight": "200.59",
"uncertainty": "0.01",
"notes": []
},
{
"z": 81,
"symbol": "Tl",
"name": "thallium",
"weight": "204.38",
"uncertainty": "0.01",
"notes": []
},
{
"z": 82,
"symbol": "Pb",
"name": "lead",
"weight": "207.2",
"uncertainty": "1.1",
"notes": []
},
{
"z": 83,
"symbol": "Bi",
"name": "bismuth",
"weight": "208.98",
"uncertainty": "0.01",
"notes": []
},
{
"z": 84,
"symbol": "Po",
"name": "polonium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 85,
"symbol": "At",
"name": "astatine",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 86,
"symbol": "Rn",
"name": "radon",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 87,
"symbol": "Fr",
"name": "francium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 88,
"symbol": "Ra",
"name": "radium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 89,
"symbol": "Ac",
"name": "actinium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 90,
"symbol": "Th",
"name": "thorium",
"weight": "232.04",
"uncertainty": "0.01",
"notes": [
"g"
]
},
{
"z": 91,
"symbol": "Pa",
"name": "protactinium",
"weight": "231.04",
"uncertainty": "0.01",
"notes": []
},
{
"z": 92,
"symbol": "U",
"name": "uranium",
"weight": "238.03",
"uncertainty": "0.01",
"notes": [
"g",
"m"
]
},
{
"z": 93,
"symbol": "Np",
"name": "neptunium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 94,
"symbol": "Pu",
"name": "plutonium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 95,
"symbol": "Am",
"name": "americium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 96,
"symbol": "Cm",
"name": "curium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 97,
"symbol": "Bk",
"name": "berkelium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 98,
"symbol": "Cf",
"name": "californium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 99,
"symbol": "Es",
"name": "einsteinium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 100,
"symbol": "Fm",
"name": "fermium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 101,
"symbol": "Md",
"name": "mendelevium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 102,
"symbol": "No",
"name": "nobelium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 103,
"symbol": "Lr",
"name": "lawrencium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 104,
"symbol": "Rf",
"name": "rutherfordium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 105,
"symbol": "Db",
"name": "dubnium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 106,
"symbol": "Sg",
"name": "seaborgium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 107,
"symbol": "Bh",
"name": "bohrium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 108,
"symbol": "Hs",
"name": "hassium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 109,
"symbol": "Mt",
"name": "meitnerium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 110,
"symbol": "Ds",
"name": "darmstadtium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 111,
"symbol": "Rg",
"name": "roentgenium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 112,
"symbol": "Cn",
"name": "copernicium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 113,
"symbol": "Nh",
"name": "nihonium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 114,
"symbol": "Fl",
"name": "flerovium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 115,
"symbol": "Mc",
"name": "moscovium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 116,
"symbol": "Lv",
"name": "livermorium",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 117,
"symbol": "Ts",
"name": "tennessine",
"weight": null,
"uncertainty": null,
"notes": []
},
{
"z": 118,
"symbol": "Og",
"name": "oganesson",
"weight": null,
"uncertainty": null,
"notes": []
}
]
}
Where it is served
The registry serves only AXLR clients, so a browser gets 403; fetch with a User-Agent that starts with axlr:
curl -A axlr-docs/1.0 -O https://registry.underpassai.com/tools/chem_formula/1.0.0/manifest.json
curl -A axlr-docs/1.0 -O https://registry.underpassai.com/tools/chem_formula/1.0.0/chem_formula.py
curl -A axlr-docs/1.0 -O https://registry.underpassai.com/tools/chem_formula/1.0.0/elements.json