Full Neurosnap API Tutorial | The Quick & Easy API For Bioinformatics
Written by Keaun Amani | Published 2024-8-6
Written by Keaun Amani | Published 2024-8-6
We are excited to announce the official release of the Neurosnap API, a highly anticipated and requested feature that allows seamless interaction with the Neurosnap platform. With this new API, users can automate complex bioinformatic pipelines from any computer without the need to install or run intricate models themselves. This release marks a significant milestone in making advanced bioinformatics more accessible and user-friendly.
Please keep in mind that this tutorial and the Neurosnap API are designed to be used by those with at least a basic understanding of programming and networking concepts.
To access the Neurosnap API you will need to first generate an API key for your account. If you've ever used a rest API in the past, this part will likely be quite familiar.
Generate API Key button.
The following is the list of all endpoints currently offered by the Neurosnap platform. Note that almost all API endpoints return data in the JSON format. Exceptions will be clearly specified below.
Job endpoints are HTTP endpoints related to neurosnap services and submitted jobs for those services.
/api/servicesLists all currently available Neurosnap services.
r = requests.get("https://neurosnap.ai/api/services", headers={"X-API-KEY": YOUR_API_KEY})
r.json()
# [
# {
# "beta": false,
# "title": "NeuroFold",
# "desc": "The most accurate approach for in silico enzyme optimization. NeuroFold makes it trivial to optimize multiple enzyme properties, simultaneously.",
# "desc_short": "Optimize enzyme thermostability, pH stability, solubility, and reaction rate with high accuracy.",
# "paper": "https://doi.org/10.1101/2024.03.12.584504",
# "video": "",
# "tags": [
# "Protein Design",
# "Protein Stability",
# "Protein Solubility"
# ],
# "features": [
# "Utilizes our proprietary NeuroFold model to optimize enzymes.",
# "40-fold increase in Spearman rank correlation when compared to Meta's ESM-1v model.",
# "Can optimize the thermostability, pH stability, and reaction rate of a target enzyme.",
# "Output sequences tend to be evolutionarily distinct and diverse.",
# "Typical sequence identity to wild type is around 49-56%.",
# "When tested on \u03b2-lactamase, NeuroFold was found to have a 100% experimental success rate.",
# "Currently only supports monomeric enzymes that do not form complex interactions with nucleotides."
# ],
# "citations": [
# "Keaun Amani, Michael Fish, Matthew D. Smith, Christian Danve M. Castroverde\nbioRxiv 2024.03.12.584504; doi: https://doi.org/10.1101/2024.03.12.584504"
# ]
# },
# ...
# ]
/api/jobsLists your accounts submitted jobs.
r = requests.get("https://neurosnap.ai/api/jobs", headers={"X-API-KEY": YOUR_API_KEY})
r.json()
# [
# {
# "Status": "completed",
# "Job ID": "66aea7b235c83718af6161d5",
# "Service": "PDBFixer",
# "Submitted": 1722722226000,
# "Runtime": "31s",
# "Note": "test job binder + membrane",
# "Shared": false
# },
# {
# "Status": "completed",
# "Job ID": "66ae52068d3b43e471e39c13",
# "Service": "AntiFold",
# "Submitted": 1722700294000,
# "Runtime": "35s",
# "Note": "test job using 8DXT",
# "Shared": false
# },
# ...
# ]
/api/job/submit/SERVICE_NAME?note=NOTESubmit a job using multipart/form-data, the exact service title, and its exact field titles. Required fields must be supplied; optional fields can be omitted. The optional note query parameter sets a job note. Success returns the job ID as a JSON string.
Open the service's API tab for a generated Python example, or inspect its form in /api/services for accepted types, formats, and limits. The examples below explain encoding; follow your service's requirements.
For DiffDock-L, upload a receptor PDB and a ligand CSV. Create molecules.csv:
name,type,value
Ligand,smiles,NC1CC(C(O)Cl)OC(N)P1
Then submit the job:
import requests
with open("receptor.pdb", "rb") as receptor, open("molecules.csv", "rb") as molecules:
response = requests.post(
"https://neurosnap.ai/api/job/submit/DiffDock-L",
headers={"X-API-KEY": YOUR_API_KEY},
params={"note": "Dock ligand against receptor"},
data={"Number Samples": "100"},
files=[
("Input Receptor", ("receptor.pdb", receptor, "chemical/x-pdb")),
("Input Ligand", ("molecules.csv", molecules, "text/csv")),
],
)
response.raise_for_status()
job_id = response.json()
print(job_id)
# '66affe9ca8c3d6ab6d8e377e'
requests sets the multipart Content-Type and boundary when files is provided; do not set that header yourself. Use a tuple list for uploads so repeated field titles are preserved. If using MultipartEncoder instead, pass a list of (field_title, value) tuples as fields and use the encoder's generated content_type header.
The service's form contains sections of field wrappers: type, field (title and settings), and optional. Grid columns use the same wrappers.
| Field type | What to submit |
|---|---|
FFMolList |
File uploads: a molecule CSV and any referenced SDF/MOL/MOL2 files, or structure files alone. |
FFProteinStructureList |
One or more PDB, CIF, or mmCIF file uploads under the same field title. |
FFSequenceList |
A regular multipart text field containing a JSON object grouped by sequence type and name. |
FFGrid |
A regular multipart text field containing a JSON object mapping column titles to arrays of cell values. |
FFFile |
A file upload matching that field's allowed extensions and size limit. |
FFSelect |
A regular multipart text field containing one exact option string. |
FFCheckbox |
A regular multipart text field containing "true" or "false". Omission means false. |
FFNumber |
A regular multipart text field containing a number as text, such as "10" or "0.5". |
FFText |
A regular multipart text field containing the requested text. |
FFHeading, FFMarkdown |
Display-only content; do not submit these fields. |
Structured JSON fields must be serialized with json.dumps(...) inside the multipart request. Do not submit the entire job using requests.post(..., json=...). Molecule and structure lists require multipart file parts, not JSON arrays or paths.
FFMolList): SMILES, CCD, and structure uploadsCheck the field's allowed_types: the possible values are smiles, ccd, and sdf. An omitted or null allowed_types permits all three. Some tools require uploaded coordinates, so always follow the field's restrictions and description. For example, molecular dynamics may require a ligand already aligned with the receptor; a CCD code or SMILES does not specify that placement.
For SMILES, CCD codes, or mixed inputs, upload one CSV with the exact header name,type,value:
name,type,value
Ethanol,smiles,CCO
ATP,ccd,ATP
Bound_ligand,sdf,ligand.sdf
Second_ligand,sdf,second_ligand.mol2
name is an optional molecule label. Keep the column even when the label is empty: ,smiles,CCO.type=smiles: value is the SMILES string.type=ccd: value is a Chemical Component Dictionary code, such as ATP.type=sdf: value is the exact uploaded structure filename, including its extension. SDF, MOL, and MOL2 files all use type=sdf; there are no separate mol or mol2 type values.CSV row order defines molecule order. Quote CSV values containing commas, quotes, or line breaks using standard CSV quoting; Python's csv.writer handles this automatically.
Upload the CSV and every referenced structure under the same molecule field title. Filenames must be unique, use filenames rather than filesystem paths in the CSV, and include each uploaded structure in the manifest. SMILES and CCD rows need no accompanying structure file.
This illustrates the upload portion of a request for a molecule field titled Input Ligand; use a service and field that allow the types and number of molecules in your CSV:
from contextlib import ExitStack
with ExitStack() as stack:
files = [
("Input Ligand", ("molecules.csv", stack.enter_context(open("molecules.csv", "rb")), "text/csv")),
("Input Ligand", ("ligand.sdf", stack.enter_context(open("ligand.sdf", "rb")), "chemical/x-mdl-sdfile")),
("Input Ligand", ("second_ligand.mol2", stack.enter_context(open("second_ligand.mol2", "rb")), "application/octet-stream")),
]
# Pass files=files to requests.post(...) inside this context.
For structure-only inputs, omit the CSV and upload SDF, MOL, or MOL2 files directly under the molecule field title. File upload order then defines molecule order. MOL and MOL2 files are converted to SDF automatically. Multi-record SDF files are split in record order: each record, including an alternate conformer, counts as a separate molecule toward min_mols and max_mols. Fields also enforce min_atoms and max_atoms.
PDB files are not accepted as small-molecule uploads. Convert them to SDF using an appropriate conversion tool first. Do not upload a ZIP or send the old JSON molecule-array format; the API accepts the CSV and structure files directly.
FFProteinStructureList)Upload PDB (.pdb) or mmCIF (.cif or .mmcif) files directly. For multiple structures, repeat the exact same field title:
with open("complex_1.pdb", "rb") as first, open("complex_2.cif", "rb") as second:
files = [
("Input Complexes", ("complex_1.pdb", first, "chemical/x-pdb")),
("Input Complexes", ("complex_2.cif", second, "chemical/x-mmcif")),
]
# Pass files=files to requests.post(...) inside this context.
Use a field that accepts at least two structures for this example. Upload order defines structure order, which matters when another input, such as a ligand list, must match it entry by entry. Files are validated against the field's entity, model, chain, and residue limits (min_entities, max_entities, min_models, max_models, and related settings). A file containing multiple models must satisfy that field's model limits; it is not treated like a multi-record SDF.
The service may also reject or remove waters, heteroatoms, or nucleic acids, and may convert between PDB and mmCIF as configured. Read the field description before submitting structures whose contents or coordinate frame must be preserved. Submit the original structure files rather than a ZIP, JSON structure array, or a local filename as a text value.
FFSequenceList)Submit a JSON object with sequence-type keys aa (amino acids), dna, and/or rna. Each type maps unique sequence names to sequence strings:
import json
sequences = {
"aa": {"protein_1": "MKTAYIAKQRQISFVKSHFSRQ"},
"dna": {"dna_1": "ACGTACGT"},
"rna": {"rna_1": "ACGUACGU"},
}
data = {"Input Sequence": json.dumps(sequences)}
Include only sequence types accepted by the field's seq_types; the example above illustrates all three, but many services accept only aa. Names must be unique across the sequence types in that field, be 2–50 characters long, and contain only letters, numbers, underscores, or hyphens. Follow the field's sequence-count and length limits (min_seqs, max_seqs, min_len, max_len) and allowed-residue rules.
If the field has residue_modifications=true, amino acid sequences can contain parenthesized CCD residue codes, such as GGCC(SEP)AA. Each parenthesized token represents one residue. This capability is specific to enabled sequence fields; it does not mean all sequence inputs accept modified residues. FASTA uploads or separate modification JSON objects are not substitutes for the sequence-list schema.
FFGrid)A grid is a JSON object of column arrays, not an array of row objects, a CSV upload, or a nested list. Use the exact column titles declared by that field. All supplied arrays must have the same length; the value at index zero in each array makes up the first row.
Example grid with text, number, select, and checkbox columns:
import json
rows = {
"Name": ["sample_1", "sample_2"],
"Weight": [1.0, 0.5],
"Mode": ["Option A", "Option B"],
"Enabled": [True, False],
}
data = {"Example Grid": json.dumps(rows)}
Replace Example Grid, the column titles, and the select options with those from your service. Text and select cells are strings; number cells accept JSON numbers or numeric strings; checkbox cells are JSON booleans (true/false, produced by json.dumps from Python True/False). Follow each column's validation rules and the grid's min_rows and max_rows. Unknown columns and missing required columns are rejected; omitted optional columns use their defaults for each row.
For FFFile, upload one file under its field title, following extensions and max_mb. Density maps, alignments, and other specialized files are uploads, not path strings:
with open("density.mrc", "rb") as density:
files = [("Density Map", ("density.mrc", density, "application/octet-stream"))]
# Pass files=files to requests.post(...) inside this context.
For ordinary text, selections, numbers, and checkboxes, use multipart text values:
data = {
"Description": "Example run", # FFText
"Number Samples": "100", # FFSelect: exact option, including numeric options
"Temperature": "300", # FFNumber
"Enable Feature": "true", # FFCheckbox; send "false" to disable
}
Use only your service's field titles. Follow each text field's syntax for residue ranges, chain identifiers, constraints, or other specialized values; comma-separated text remains a string unless documented otherwise.
If a request has no file inputs, force multipart encoding with requests by representing text fields as (None, value) file entries:
multipart_fields = [(title, (None, value)) for title, value in data.items()]
# requests.post(url, headers={"X-API-KEY": YOUR_API_KEY}, files=multipart_fields)
Using data=data alone sends URL-encoded form data, which the job submission endpoint does not accept. Check errors with raise_for_status() and inspect the response body for field-specific validation details before retrying a submission.
/api/job/status/JOB_ID;Fetches the status of a specified job. Job statuses can include pending, running, failed, completed, deleted, and cancelled.
r = requests.get(
"https://neurosnap.ai/api/job/status/66affe9ca8c3d6ab6d8e377e",
headers={"X-API-KEY": YOUR_API_KEY},
)
r.json()
# 'completed'
/api/job/cancel/JOB_ID;Cancels a running job on Neurosnap. This will mark the job as canceled and deduct the credits used so far.
r = requests.post(
"https://neurosnap.ai/api/job/cancel/66affe9ca8c3d6ab6d8e377e",
headers={"X-API-KEY": YOUR_API_KEY},
)
r.raise_for_status() # no content in response but can raise if status code is not 200
/api/job/data/JOB_ID/?share=SHARE_IDFetches all the configuration and files associated with a Neurosnap job. For shared / public jobs you can optionally provide the share ID as a query parameter as well. This is not required and job sharing is disabled by default.
r = requests.get(
"https://neurosnap.ai/api/job/data/68dd7c98c435f1b0ee471c17", # fetch output files of a job
headers={"X-API-KEY": YOUR_API_KEY},
)
r.json()
# {
# "config": {
# "Density Map": "Density_Map.map"
# },
# "in": [
# [
# "Density_Map.map",
# "64.00 MB"
# ]
# ],
# "out": [
# [
# "output.mrc",
# "42.30 MB"
# ]
# ]
# }
/api/job/file/JOB_ID/[in/out]/FILE_NAME?share=SHARE_ID;Fetches a specific file to download from a completed Neurosnap job. This endpoint requires you to specify whether you want to fetch an input file (file you uploaded to run the job) or fetch an output file (file produced during the Neurosnap job by the service). For shared / public jobs you can optionally provide the share ID as a query parameter as well. This is not required and job sharing is disabled by default.
r = requests.get(
"https://neurosnap.ai/api/job/file/66affe9ca8c3d6ab6d8e377e/out/output.json", # fetch a file called output.json
headers={"X-API-KEY": YOUR_API_KEY},
)
# save the file to disk
with open("output.json", "wb") as f:
f.write(r.content)
/api/job/note/set;Set a note for a submitted job. Notes can be thought of as mini descriptions that you can assign to your jobs for convenience and organizational purposes.
r = requests.post(
"https://neurosnap.ai/api/job/status/66affe9ca8c3d6ab6d8e377e",
headers={"X-API-KEY": YOUR_API_KEY},
json={"job_id": "66affe9ca8c3d6ab6d8e377e", "note": "Binder design against target PDCD1."}
)
r.raise_for_status() # no content in response but can raise if status code is not 200
/api/job/share/set/JOB_ID;Enables the sharing feature of a job and makes it public to anybody with the job ID and share ID. Note that sharing is always disabled by default and that enabling it means that anybody with the job ID and share ID will be able to access the job results and inputs (only for jobs with sharing enabled).
r = requests.get(
"https://neurosnap.ai/api/job/share/set/66affe9ca8c3d6ab6d8e377e",
headers={"X-API-KEY": YOUR_API_KEY},
)
r.json()
# '66affe9ca8c3d6ab6d8e377e' #the new share ID of the job
/api/job/share/delete/JOB_ID;Disables the sharing feature of a job and makes the job private. Note that sharing is always disabled by default.
r = requests.get(
"https://neurosnap.ai/api/job/share/delete/66affe9ca8c3d6ab6d8e377e",
headers={"X-API-KEY": YOUR_API_KEY},
)
r.raise_for_status() # no content in response but can raise if status code is not 200
/api/job/copy/JOB_ID;Duplicates an existing job and immediately re-runs the duplicate using the same inputs and configuration. This endpoint checks job access the same way as the rest of the platform, so team members can duplicate team jobs they have access to. Credit validation is based on the previous runtime of the original job. If your personal or team credit balance is lower than that estimate, the request will be rejected. If successful, this endpoint returns the new duplicated job ID in JSON format.
r = requests.post(
"https://neurosnap.ai/api/job/copy/66affe9ca8c3d6ab6d8e377e",
headers={"X-API-KEY": YOUR_API_KEY},
)
r.json()
# '67b1d22fa8c3d6ab6d8e488f'
Project endpoints are HTTP endpoints for organizing jobs into Projects. Each job can belong to one Project at a time. Projects can be personal or team-visible. Personal projects can only contain your personal jobs. Team-visible projects can only contain jobs from your current Neurosnap Team.
/api/job-projectsLists all Projects visible to your account, including personal Projects and team-visible Projects for your current team.
r = requests.get(
"https://neurosnap.ai/api/job-projects",
headers={"X-API-KEY": YOUR_API_KEY},
)
r.json()
# [
# {
# "id": "68ba2c90c435f1b0ee471c17",
# "user_id": "66affe9ca8c3d6ab6d8e377e",
# "team_id": "000000000000000000000000",
# "team_visible": false,
# "name": "PD-1 Binder Round 1",
# "description": "Initial design and screening jobs for PD-1 binders.",
# "created_at": "2026-09-04T14:12:33.123Z",
# "updated_at": "2026-09-04T14:12:33.123Z",
# "job_count": 8,
# "can_manage": true
# },
# ...
# ]
/api/job-project/createCreates a new Project. Set team_visible to true to create a team-visible Project. You must currently belong to a team to create a team-visible Project.
r = requests.post(
"https://neurosnap.ai/api/job-project/create",
headers={"X-API-KEY": YOUR_API_KEY},
json={
"name": "PD-1 Binder Round 1",
"description": "Initial design and screening jobs for PD-1 binders.",
"team_visible": false,
},
)
r.json()
# {
# "id": "68ba2c90c435f1b0ee471c17",
# "user_id": "66affe9ca8c3d6ab6d8e377e",
# "team_id": "000000000000000000000000",
# "team_visible": false,
# "name": "PD-1 Binder Round 1",
# "description": "Initial design and screening jobs for PD-1 binders.",
# "created_at": "2026-09-04T14:12:33.123Z",
# "updated_at": "2026-09-04T14:12:33.123Z"
# }
/api/job-project/updateUpdates a Project's name and description. Project visibility cannot be changed through this endpoint. Personal Projects can be updated by their owner. Team-visible Projects can be updated by the team leader.
r = requests.post(
"https://neurosnap.ai/api/job-project/update",
headers={"X-API-KEY": YOUR_API_KEY},
json={
"project_id": "68ba2c90c435f1b0ee471c17",
"name": "PD-1 Binder Round 2",
"description": "Second design and screening round for PD-1 binders.",
},
)
r.json()
# {
# "id": "68ba2c90c435f1b0ee471c17",
# "user_id": "66affe9ca8c3d6ab6d8e377e",
# "team_id": "000000000000000000000000",
# "team_visible": false,
# "name": "PD-1 Binder Round 2",
# "description": "Second design and screening round for PD-1 binders.",
# "created_at": "2026-09-04T14:12:33.123Z",
# "updated_at": "2026-09-04T14:18:44.456Z"
# }
/api/job-project/assignAssigns one or more jobs to a Project. Since each job can only belong to one Project at a time, assigning a job to a new Project moves it out of its previous Project. To remove jobs from a Project without assigning them to another Project, set project_id to 000000000000000000000000.
r = requests.post(
"https://neurosnap.ai/api/job-project/assign",
headers={"X-API-KEY": YOUR_API_KEY},
json={
"project_id": "68ba2c90c435f1b0ee471c17",
"job_ids": [
"66affe9ca8c3d6ab6d8e377e",
"67b1d22fa8c3d6ab6d8e488f",
],
},
)
r.json()
# {
# "updated": 2
# }
/api/job-project/deleteDeletes a Project. Deleting a Project does not delete its jobs. Jobs assigned to the deleted Project are kept and marked as unassigned. Personal Projects can be deleted by their owner. Team-visible Projects can be deleted by the team leader.
r = requests.post(
"https://neurosnap.ai/api/job-project/delete",
headers={"X-API-KEY": YOUR_API_KEY},
json={"project_id": "68ba2c90c435f1b0ee471c17"},
)
r.raise_for_status() # no content in response but can raise if status code is not 200
Team endpoints are HTTP endpoints related to viewing and managing Neurosnap Teams.
/api/teams/info;Fetches your team's information if you are part of a Neurosnap Team.
r = requests.get(
"https://neurosnap.ai/api/teams/info",
headers={"X-API-KEY": YOUR_API_KEY},
)
r.json()
# {
# "name": "Mario Brothers Plumbing Inc.",
# "is_leader": true,
# "seatsMax": 2
# "computeCreditsRemaining": 250,
# "members": [
# {
# "ID": "66affe9ca8c3d6ab6d8e377e",
# "Name": "Mario",
# "Email": "mario@neurosnap.ai",
# "LastLogin": 1722810058,
# "EmailVerified": true,
# "Leader": true
# },
# {
# "ID": "64da8b62ad82b9c2bc36b143",
# "Name": "Luigi",
# "Email": "luigi@neurosnap.ai",
# "LastLogin": 1722616210,
# "EmailVerified": true,
# "Leader": false
# }
# ],
# }
/api/teams/jobs;Fetches all the jobs submitted by all members of your Neurosnap Team.
r = requests.get(
"https://neurosnap.ai/api/teams/jobs",
headers={"X-API-KEY": YOUR_API_KEY},
)
r.json()
# [
# {
# "Status": "completed",
# "Job ID": "66aea7b235c83718af6161d5",
# "Service": "PDBFixer",
# "Submitted": 1722722226000,
# "Runtime": "31s",
# "Note": "test job binder + membrane",
# "Shared": false
# },
# {
# "Status": "completed",
# "Job ID": "66ae52068d3b43e471e39c13",
# "Service": "AntiFold",
# "Submitted": 1722700294000,
# "Runtime": "35s",
# "Note": "test job using 8DXT",
# "Shared": false
# },
# ...
# ]
Congrats on making it this far! You should now be equipped with everything you need to know in order to successfully and effectively utilize the Neurosnap API. Just keep in mind that we have plans to greatly expand the capabilities of our API and that changes to the existing API structure are certainly possible. Make sure to keep referencing this page for updates.
By Keaun Amani
By Amélie Lagacé-O'Connor
By Danial Gharaie Amirabadi
By Keaun Amani
By Keaun Amani
By Keaun Amani
Register for free — upgrade anytime.
Interested in getting a license? Contact Sales.
Try Free