Reference
SDS to XML Converter API
Every extraction also comes back as XML. This page documents exactly how that XML is built, so your parser does not have to guess.
Last updated: 2026-10-24
The structure, by example
This is the XML for a small record (indented here for reading; the API returns it without whitespace between elements). It is the same data as outputs.json, with confidence_score and warnings added as final elements.
<?xml version="1.0" encoding="UTF-8"?>
<sds_extraction>
<product_identification>
<product_name>Acetone</product_name>
<supplier_name>Example Chemicals</supplier_name>
</product_identification>
<hazards_identification>
<ghs_classification>Flammable Liquid - Category 2</ghs_classification>
<ghs_classification>Eye Irritation - Category 2A</ghs_classification>
<signal_word>Danger</signal_word>
</hazards_identification>
<composition>
<substance_name>Acetone</substance_name>
<cas_number>67-64-1</cas_number>
<concentration_percent>95-100</concentration_percent>
</composition>
<physical_chemical_properties>
<flash_point_c>-20</flash_point_c>
<odor></odor>
</physical_chemical_properties>
<transport_information>
<un_number>UN1090</un_number>
<hazard_class>3</hazard_class>
<packing_group>II</packing_group>
</transport_information>
<confidence_score>0.94</confidence_score>
<warnings>Section 9: boiling point unreadable</warnings>
</sds_extraction>
The rules
- The root element is
sds_extraction, and element names are the JSON keys, in the same order. - An array becomes repeated elements with the same name: two
ghs_classificationvalues give twoghs_classificationelements. - An array of objects repeats the parent: each substance is its own
compositionelement containing its fields. - A
nullor empty string becomes an empty element, such as<odor></odor>, and an empty array becomes one empty element. - There are no attributes and no namespaces. Values are XML-escaped (
&<>and quotes). - Tag names are sanitized: spaces become underscores, characters outside letters, digits,
_,.and-become underscores, and a name that does not start with a letter gets a leading underscore. - No XSD or DTD is published. The contract is the set of 16 section keys; fields inside a section follow the document.
Consequences to plan for
- One item looks like a scalar. A list with one entry is a single element, the same as a plain value. Always read repeated elements with a find-all, never assume a count.
- Empty is ambiguous. An empty element can mean null, an empty string or an empty array. If the difference matters, use the JSON output.
- Confidence and warnings are in the document. In XML and CSV they are appended to the record. In the JSON response they are top-level fields beside
outputs.
Parsing it
Python's standard library is enough. This snippet runs against a real response:
import xml.etree.ElementTree as ET
root = ET.fromstring(result["outputs"]["xml"]) # result = client.extract("sds.pdf")
un_number = root.findtext("transport_information/un_number")
confidence = float(root.findtext("confidence_score"))
warnings = [w.text for w in root.findall("warnings") if w.text]
# composition repeats once per substance, and a single substance is still one <composition>
substances = [{child.tag: child.text for child in item} for item in root.findall("composition")]
| You want | Path (ElementTree) |
|---|---|
| UN number | transport_information/un_number |
| All hazard classifications | hazards_identification/ghs_classification (find-all) |
| Every substance | composition (find-all, then read children) |
| Warnings | warnings (find-all) |
XML, JSON or CSV?
| Format | Choose it when | Watch out for |
|---|---|---|
| JSON | You are building a new integration or need to tell null from empty. | Inner field names follow the document, so map defensively. |
| XML | Your ERP, import tool or middleware expects XML. | Single-item arrays look like scalars; empty elements are ambiguous. |
| CSV | You want a flat field,value list for a spreadsheet or quick review. | See below. |
How CSV is written
field,value
product_identification.product_name,Acetone
hazards_identification.ghs_classification,"[""Flammable Liquid - Category 2"",""Eye Irritation - Category 2A""]"
composition[0].substance_name,Acetone
composition[0].cas_number,67-64-1
physical_chemical_properties.flash_point_c,'-20
physical_chemical_properties.odor,
transport_information.un_number,UN1090
confidence_score,0.94
warnings,"[""Section 9: boiling point unreadable""]"
- The header is
field,value, with one row per value and dotted paths such astransport_information.un_number. - An array of objects is indexed, for example
composition[0].cas_number. An array of plain values is written as a JSON string. - A value that starts with
=,+,-or@gets a leading apostrophe, which stops spreadsheets running it as a formula. That is why-20appears as'-20: strip a leading apostrophe when you import numbers.
How you get access: the free sample upload on the homepage needs no key, the self-serve plan issues an API key after signup, and enterprise customers get a dedicated deployment with its own base URL. See pricing.
FAQ
Is there an XSD for the XML?
No schema file is published. Element names follow the JSON keys, and only the 16 section keys are part of the contract.
Does the XML include the confidence score and warnings?
Yes, as confidence_score and warnings elements at the end of the record.
Why does my one-item list parse as a single value?
Arrays are written as repeated elements, so a list of one is one element. Use a find-all and treat the result as a list.