Skip to navigation

Working with Molecules

Create, register, update, and archive molecules, one at a time or in bulk.

Molecules are GA, so no header is needed. For how Compounds, Forms, and counterparts relate, see the concept model.

OperationEndpoint
Create onePOST /molecule
Get oneGET /molecule/{molecule_id}
ListGET /molecule/items
Update onePATCH /molecule/{molecule_id}

Higher-volume create and update variants are covered in Choosing a create endpoint.

The structure

Every molecule carries a chemicalStructure on input:

  • structureFormat: smiles or molfile.
  • value: the structure itself. An empty SMILES creates a structureless molecule (a valid, named placeholder with no structure).

On read, the molecule reports three SMILES fields. The request’s chemicalStructure is not echoed back.

FieldWhat it is
canonicalSmilesThe canonicalized structure with salts and solvents stripped (base only)
originalSmilesExactly what you supplied (null if you supplied only a molfile)
smilesWithCounterpartsThe full structure: base plus salt/solvent counterparts as fragments, repeated per ratio. Equals canonicalSmiles when there are no counterparts

Creating a molecule

curl -X POST 'https://{tenant_name}.benchling.com/api/v3/molecule' \
-u "$API_KEY:" \
-H 'Content-Type: application/json' \
-d '{
"name": "Aspirin",
"chemicalStructure": { "structureFormat": "smiles", "value": "CC(=O)OC1=CC=CC=C1C(=O)O" },
"folderId": "lib_XXXXXXXX"
}'

name, chemicalStructure, and folderId are required. schemaId and schemaFields are optional; see Working with Molecule Schemas for discovering these.

Registering a molecule

Registration is controlled by three fields on create or update:

  • registryId is the on/off switch. When present, the molecule is registered into that registry. Setting it to null on update unregisters the molecule.
  • Plus exactly one of:
    • namingStrategy: how Benchling assigns the registry ID and name (for example, NEW_IDS to auto-generate a new ID and keep the name). See naming strategies for all of them.
    • entityRegistryId: supply your own ID.

Providing both, or neither, is rejected.

Updating and archiving a molecule

To archive or restore a molecule, use the same update endpoint: PATCH /molecule/{molecule_id} with archived: true (optionally with an archiveReason) to archive, or archived: false to restore.

Why can’t I change a molecule’s structure?

If the molecule is linked to a reaction, editing its chemicalStructure returns a 400 (“linked to a reaction”). Editing its name or schema fields still works; only the structure edit is blocked. (Salt and solvent library structures are separately immutable after creation, by design.)

Choosing a create endpoint

There are four ways to create molecules. Pick by how many you’re creating, and by whether you want Benchling to auto-create the parent Compound for salt forms.

I want to create…EndpointWhat you get backStability
One moleculePOST /moleculeThe created molecule, immediatelyGA
A handful (up to 25) togetherPOST /molecule:batch-createAll created molecules, immediately. All-or-nothingGA
ManyPOST /molecule:bulk-createA task to poll for resultsGA
Many, auto-creating the parent Compound for salt formsPOST /molecule:bulk-create-molecules-with-parentA task to poll for resultsBeta

“Batch” here means an operation, not an entity. :batch-create is a synchronous multi-create. It is not related to a Batch/Lot, a physical sample of a registered entity, which isn’t exposed on the V3 API at all. Creating molecules with :batch-create never touches Batches.

On the standard endpoints, a Form is linked to its parent Compound only if you supply the Compound’s ID yourself (in schemaFields). Nothing is auto-resolved. :bulk-create-molecules-with-parent is the only endpoint that resolves the parent for you: it finds a matching Compound by structure and reuses it, or creates one if none exists.

Using the bulk endpoints

Unlike :batch-create, the :bulk-* endpoints don’t take an inline array. You stage your rows as a file first. The Bulk Import API guide covers the flow in full.

:bulk-create itself is GA, but the scratch-file upload and the task-polling endpoints it depends on are beta. Send EARLY-ACCESS: true on steps 1 and 3.

  1. Build a JSONL payload: one {"request_id": "...", "input_object": { ... }} per line, where input_object has the same fields as a single create and each request_id is unique. Upload it with POST /scratch-file:create-and-upload and { "filename": "molecules.jsonl", "contents": "<your JSONL>" } (inline contents is UTF-8, up to 10 MiB). You get back a scrfile_... ID.
  2. Call POST /molecule:bulk-create with the body { "fileId": "scrfile_..." }. It returns 202 with a taskId and pollingUri.
  3. Poll GET /tasks/bulk/{task_id} until it completes. Fetch the created IDs from .../successful-results and any failures from .../errors. Both are keyed by your request_id.