Inferencing with the NIM#

This documentation provides a comprehensive guide for using the Boltz-2 NIM for protein structure prediction and related tasks.

Before You Start#

Deploy and start the Boltz-2 NIM container before using the API. For prerequisites, NGC authentication, docker run, and readiness checks, refer to Getting Started.

NIM API Reference#

This documentation contains the API reference for the Boltz-2 NIM.

OpenAPI Specification#

You can download the complete OpenAPI Specification for the Boltz-2 NIM here.

Example Notebooks#

Below, we provide example notebooks that demonstrate how to use the Boltz-2 NIM API in various scenarios:

  • Protein-Ligand Covalent Complex Visualization with Mol*Star - Use Boltz-2 NIM to predict and visualize a protein-ligand covalent complex structure using Mol*Star viewer

    Protein-Ligand Covalent Complex View | Download

  • DNA-Protein Complex Visualization with py3Dmol - Use Boltz-2 NIM to predict and visualize a DNA-Protein complex structure using py3Dmol viewer

    DNA-Protein Complex View | Download

These notebooks provide hands-on examples of:

  • Setting up the NIM environment

  • Making API calls to the NIM

  • Processing and visualizing the results

  • Working with different types of molecular structures

  • Using various visualization tools

Predict a Protein Structure#

Endpoint path: /biology/mit/boltz2/predict

Request type: post

Input Parameters#

  • polymers (list[Polymer]): Required. A list of polymers (DNA, RNA, or Protein). Minimum 1, maximum 12 polymers allowed. Each polymer contains:

    • id (string): Optional. Unique identifier for the polymer chain. Must contain 1-4 alphanumeric characters. If omitted, the NIM assigns an ID automatically.

    • molecule_type (string): Required. Type of molecule - “dna”, “rna”, or “protein”.

    • sequence (string): Required. The sequence of the polymer. Must be 1-4,096 characters and contain only valid characters for the molecule type. For proteins, valid characters are standard one-letter amino acid codes plus X for ambiguous residues. For DNA, use A, T, C, and G. For RNA, use A, U, C, and G.

    • cyclic (boolean): Optional (default: false). Whether the polymer forms a cyclic structure.

    • msa (dictionary): Optional. Multiple Sequence Alignments for protein molecules only, with a maximum of three database entries. This nested dictionary contains a mapping of database names to alignment formats and records (for example {"database_1" : {"a3m" : {"format" : "a3m", "alignment" : ">1\nMVNIDIAIAMAI", "rank" : 0}}}). The alignment format key and the format field must use lowercase values from csv, fasta, a3m, or sto (case-sensitive). Only a3m and csv alignments are consumed during inference; fasta and sto are accepted by the schema but removed during input preparation. The first sequence in each alignment must match the polymer sequence. Each alignment record may include an optional rank field (default: -1).

    • modifications (list[Modification]): Optional. Chemical modifications to specific residues in the sequence. Each modification contains:

      • ccd (string): Required. Chemical Component Dictionary (CCD) ID of the modification (1-5 uppercase alphanumeric characters).

      • position (int): Required. The 1-based index of the residue to modify.

    • structural_templates (list[StructuralTemplate]): Optional. Up to four structural templates to guide prediction. Structural templates are supported for protein polymers only. Each template contains:

      • structure (string): Required. The structural template content in the specified format.

      • format (string): Optional (default: cif). The format of the template structure. Supported formats: cif or pdb.

      • name (string): Optional. Identifier or name for the template (for example, a PDB ID). Maximum 64 characters; only alphanumeric characters, underscores, and hyphens are supported.

      • chain_id (string): Optional. Chain ID from the template structure, containing 1-4 alphanumeric characters. If omitted, the NIM selects the best-matching chain based on sequence similarity.

  • ligands (list[Ligand]): Optional (default: empty list). A list of ligands. Maximum 20 ligands allowed. Each ligand contains:

    • id (string): Optional. A chain ID for the ligand. If omitted, the NIM assigns IDs such as B1, B2, and so on.

    • ccd (string): Optional. Chemical Component Dictionary (CCD) code for the ligand (1-5 uppercase alphanumeric characters). Must be uppercase.

    • smiles (string): Optional. SMILES string representation of the ligand. Either CCD or SMILES must be provided, but not both.

    • predict_affinity (boolean): Optional (default: false). Enable affinity prediction for this ligand. When enabled, the model predicts binding affinity between the protein and this ligand. Only one ligand per request can have predict_affinity=True. Duplicate CCD or SMILES values across ligands are not allowed when affinity prediction is enabled.

    • output_affinity_embedding (boolean): Optional (default: false). When enabled together with predict_affinity on the same ligand, include 384-dimensional affinity embedding vectors under the ligand’s entry in the affinities response field.

  • constraints (list[Union[Pocket, Bond]]): Optional (default: empty list). Optional constraints for the prediction. Can be either:

    • Bond Constraint: Specifies atomic bonds between molecules

      • constraint_type: “bond”

      • atoms (list[Atom]): List of exactly two atoms involved in the bond. Each atom contains:

        • id (string): Required. Chain identifier for the atom.

        • residue_index (int): Required. 1-based index of the residue containing the atom.

        • atom_name (string): Required. Name of the specific atom.

    • Pocket Constraint: Defines binding site interactions

      • constraint_type: “pocket”

      • binder (string): Required. The ID of the binding ligand. Only one pocket binder is allowed per request.

      • contacts (list[Contact]): Required. List of contacts defining the pocket. Each contact contains:

        • id (string): Required. Chain identifier for the polymer contact residue.

        • residue_index (int): Required. 1-based index of the residue in the binding site.

  • recycling_steps (int): Optional (default: 3). The number of recycling steps to use for prediction. Range: 1-10.

  • sampling_steps (int): Optional (default: 50). The number of sampling steps to use for prediction. Range: 10-1,000.

  • diffusion_samples (int): Optional (default: 1). The number of diffusion samples to use for prediction. Range: 1-25.

  • step_scale (float): Optional (default: 1.638). Controls the magnitude of change applied at each diffusion sampling step. Larger values can speed up convergence but may reduce coordinate precision; smaller values can improve precision but require more sampling steps. Recommended range: 1.0-2.0. Range: 0.5-5.0.

  • without_potentials (boolean): Optional (default: false). Returns the results without potentials.

  • output_format (string): Optional (default: “mmcif”). The output format of the returned structure. Currently only “mmcif” is supported.

  • concatenate_msas (boolean): Optional (default: false). Concatenate Multiple Sequence Alignments for a polymer into one alignment.

  • sampling_steps_affinity (int): Optional (default: 200). The number of sampling steps to use for affinity prediction. Range: 10-1,000. Higher values may improve accuracy but increase runtime. Reduce for faster results.

  • diffusion_samples_affinity (int): Optional (default: 5). The number of diffusion samples to use for affinity prediction. Range: 1-10. Higher values may improve reliability but increase runtime. Reduce to 1 for faster results.

  • affinity_mw_correction (boolean): Optional (default: false). Enable molecular weight correction for affinity prediction, especially useful for metal-containing ligands.

  • write_full_pae (boolean): Optional (default: false). Include the full Predicted Aligned Error (PAE) matrix for each diffusion sample in the pae response field. Full matrices scale as O(tokens²) per diffusion sample and can produce very large JSON responses for large complexes.

  • write_full_pde (boolean): Optional (default: false). Include the full Predicted Distance Error (PDE) matrix for each diffusion sample in the pde response field. Full matrices scale as O(tokens²) per diffusion sample and can produce very large JSON responses for large complexes.

Note

PAE and PDE values are expressed in ångströms (Å). Lower values generally indicate higher confidence in the predicted structure or pairwise relationships. Aggregate PDE scores (complex_pde_scores, complex_ipde_scores) are always returned in the JSON response. Full PAE/PDE matrices are returned only when the corresponding request flags are enabled. Aggregate PAE scores (such as complex_pae or complex_ipae in Boltz OSS confidence files) are not exposed as separate fields in the NIM JSON API.

Outputs#

  • structures (list[Structure]): The predicted protein structures. Each structure contains:

    • structure (string): The contents of a single structural prediction in the specified format.

    • format (string): The format of the structure record (currently “mmcif”).

    • name (string): Optional name for the structure.

    • source (string): Optional source file for the structure.

  • confidence_scores (list[float]): Confidence scores for each predicted structure.

  • ptm_scores (list[float]): Predicted TM score for the complete complex for each diffusion sample.

  • iptm_scores (list[float]): Predicted TM score aggregated at interfaces for each diffusion sample.

  • ligand_iptm_scores (list[float]): Interface TM score aggregated at protein-ligand interfaces for each diffusion sample.

  • protein_iptm_scores (list[float]): Interface TM score aggregated at protein-protein interfaces for each diffusion sample.

  • complex_plddt_scores (list[float]): Average pLDDT score for the complete complex for each diffusion sample.

  • complex_iplddt_scores (list[float]): Average pLDDT score with interface tokens upweighted for each diffusion sample.

  • complex_pde_scores (list[float]): Contact-weighted average PDE score for the complete complex, in ångströms. Always returned with one scalar per diffusion sample; no request flag is required. Lower values generally indicate higher confidence.

  • complex_ipde_scores (list[float]): Contact-weighted average PDE score aggregated at interfaces (ipDE), in ångströms. Always returned with one scalar per diffusion sample; no request flag is required. Lower values generally indicate higher confidence.

  • chains_ptm_scores (list[float]): Predicted TM score within each chain.

  • pair_chains_iptm_scores (list[dict[str, dict[str, float]]]): Predicted interface TM scores between each pair of chains. The response contains one dictionary keyed by chain ID.

  • pae (list[list[list[float]]]): The full PAE matrix for each diffusion sample, in ångströms. This field is included only when write_full_pae is true; otherwise it is omitted from the response. Its shape is [diffusion_samples][tokens][tokens]; pae[sample][i][j] is the expected position error at token i when the predicted and true structures are aligned on token j. Lower values generally indicate higher confidence.

  • pde (list[list[list[float]]]): The full PDE matrix for each diffusion sample, in ångströms. This field is included only when write_full_pde is true; otherwise it is omitted from the response. Its shape is [diffusion_samples][tokens][tokens]; pde[sample][i][j] is the expected distance error between tokens i and j. Lower values generally indicate higher confidence.

  • affinities (dict[str, AffinityPrediction]): Always returned as an object. When no ligand has predict_affinity enabled, this object is empty. When affinity prediction is enabled, keys are ligand IDs and values contain:

    • affinity_pic50 (list[float]): Predicted pIC50 binding affinity.

    • affinity_pred_value (list[float]): Predicted log(IC50).

    • affinity_probability_binary (list[float]): Binding probability in the range 0-1.

    • model_1_affinity_pred_value (list[float]): Predicted log(IC50) from affinity ensemble model 1.

    • model_1_affinity_probability_binary (list[float]): Binding probability from affinity ensemble model 1.

    • model_2_affinity_pred_value (list[float]): Predicted log(IC50) from affinity ensemble model 2.

    • model_2_affinity_probability_binary (list[float]): Binding probability from affinity ensemble model 2.

    • affinity_embedding (list[list[float]] | null): Combined affinity embedding, with one 384-dimensional vector per diffusion sample. Returned when output_affinity_embedding is enabled on the affinity ligand.

    • model_1_affinity_embedding (list[list[float]] | null): Embedding from affinity ensemble model 1.

    • model_2_affinity_embedding (list[list[float]] | null): Embedding from affinity ensemble model 2.

  • metrics (dictionary): Runtime metrics for the request. Common keys include total_time_seconds, input_preparation_time_seconds, dataloader_setup_time_seconds, model_inference_time_seconds, postprocessing_time_seconds, response_construction_time_seconds, and affinity_prediction_time_seconds when affinity prediction runs.

Validation and Errors#

  • Unknown request fields are rejected with HTTP 422 because the request schema does not allow additional properties.

  • Validation failures and processing errors return JSON shaped like {"error": "<message>"} rather than a FastAPI detail array.

  • Common validation failures include invalid sequence characters, duplicate ligand identifiers for affinity prediction, using output_affinity_embedding without predict_affinity on the same ligand, and providing structural templates for non-protein polymers.

Examples#

Basic Protein Structure Prediction#
#!/bin/bash

# Create JSON payload for protein structure prediction
JSON='{
"polymers": [
    {
    "id": "A",
    "molecule_type": "protein",
    "sequence": "MALWMRLLPLLALLALWGPDPAAAFVNQHLCGSHLVEALYLVCGERGFFYTPKTRREAEDLQVGQVELGGGPGAGSLQPLALEGSLQKRGIVEQCCTSICSLYQLENYCN"
    }
],
"recycling_steps": 3,
"sampling_steps": 50,
"diffusion_samples": 1,
"step_scale": 1.638,
"output_format": "mmcif"
}'

# Make request
echo "Making request..."
curl -s -X POST \
-H "Content-Type: application/json" \
-d "$JSON" \
http://localhost:8000/biology/mit/boltz2/predict
import requests
import json


if __name__ == "__main__":
    sequence = "MALWMRLLPLLALLALWGPDPAAAFVNQHLCGSHLVEALYLVCGERGFFYTPKTRREAEDLQVGQVELGGGPGAGSLQPLALEGSLQKRGIVEQCCTSICSLYQLENYCN"  # Replace with your sequence value of interest
    headers = {
    "content-type": "application/json"
    }
    data = {
    "polymers": [
        {
        "id": "A",
        "molecule_type": "protein",
        "sequence": sequence
        }
    ],
    "recycling_steps": 3,
    "sampling_steps": 50,
    "diffusion_samples": 1,
    "step_scale": 1.638,
    "output_format": "mmcif"
    }
    print("Making request...")
    response = requests.post("http://localhost:8000/biology/mit/boltz2/predict", headers=headers, data=json.dumps(data))
    result = response.json()
    print("Structure prediction completed")
    # Access the first predicted structure
    if result.get("structures"):
        structure = result["structures"][0]
        print(f"Structure format: {structure['format']}")
        print(f"Confidence score: {result['confidence_scores'][0]}")
        print(f"Complex PDE score: {result['complex_pde_scores'][0]}")
Protein-Ligand Complex Prediction#
#!/bin/bash

# Create JSON payload for protein-ligand complex prediction
JSON='{
"polymers": [
    {
    "id": "A",
    "molecule_type": "protein",
    "sequence": "MALWMRLLPLLALLALWGPDPAAAFVNQHLCGSHLVEALYLVCGERGFFYTPKTRREAEDLQVGQVELGGGPGAGSLQPLALEGSLQKRGIVEQCCTSICSLYQLENYCN"
    }
],
"ligands": [
    {
    "id": "B1",
    "smiles": "CC(=O)OC1=CC=CC=C1C(=O)O"
    }
],
"recycling_steps": 3,
"sampling_steps": 50,
"output_format": "mmcif"
}'

# Make request
echo "Making request..."
curl -s -X POST \
-H "Content-Type: application/json" \
-d "$JSON" \
http://localhost:8000/biology/mit/boltz2/predict
import requests
import json


if __name__ == "__main__":
    sequence = "MALWMRLLPLLALLALWGPDPAAAFVNQHLCGSHLVEALYLVCGERGFFYTPKTRREAEDLQVGQVELGGGPGAGSLQPLALEGSLQKRGIVEQCCTSICSLYQLENYCN"
    headers = {
    "content-type": "application/json"
    }
    data = {
    "polymers": [
        {
        "id": "A",
        "molecule_type": "protein",
        "sequence": sequence
        }
    ],
    "ligands": [
        {
        "id": "B1",
        "smiles": "CC(=O)OC1=CC=CC=C1C(=O)O"  # Aspirin
        }
    ],
    "recycling_steps": 3,
    "sampling_steps": 50,
    "output_format": "mmcif"
    }
    print("Making request...")
    response = requests.post("http://localhost:8000/biology/mit/boltz2/predict", headers=headers, data=json.dumps(data))
    result = response.json()
    print("Protein-ligand complex prediction completed")

Example with Multiple Features#

#!/bin/bash

# Example combining polymers, a ligand, constraints, and output options
JSON='{
"polymers": [
    {
    "id": "A",
    "molecule_type": "protein",
    "sequence": "MALWMRLLPLLALLALWGPDPAAAFVNQHLCGSHLVEALYLVCGERGFFYTPKTRREAEDLQVGQVELGGGPGAGSLQPLALEGSLQKRGIVEQCCTSICSLYQLENYCN",
    "cyclic": false,
    "modifications": [
        {
        "ccd": "SEP",
        "position": 15
        }
    ]
    }
],
"ligands": [
    {
    "id": "L1",
    "smiles": "CC(=O)OC1=CC=CC=C1C(=O)O"
    }
],
"constraints": [
    {
    "constraint_type": "pocket",
    "binder": "L1",
    "contacts": [
        {
        "id": "A",
        "residue_index": 25
        }
    ]
    }
],
"recycling_steps": 3,
"sampling_steps": 50,
"diffusion_samples": 1,
"step_scale": 1.638,
"without_potentials": false,
"output_format": "mmcif",
"concatenate_msas": false,
"write_full_pae": true,
"write_full_pde": true
}'

echo "Making comprehensive prediction request..."
curl -s -X POST \
-H "Content-Type: application/json" \
-d "$JSON" \
http://localhost:8000/biology/mit/boltz2/predict | jq '.'
import requests
import json


if __name__ == "__main__":
    # Example combining polymers, ligands, constraints, and output options
    headers = {
        "content-type": "application/json"
    }
    data = {
        # Required: At least one polymer
        "polymers": [
            {
                "id": "A",                          # Chain identifier
                "molecule_type": "protein",         # DNA, RNA, or protein
                "sequence": "MALWMRLLPLLALLALWGPDPAAAFVNQHLCGSHLVEALYLVCGERGFFYTPKTRREAEDLQVGQVELGGGPGAGSLQPLALEGSLQKRGIVEQCCTSICSLYQLENYCN",
                "cyclic": False,                    # Whether polymer is cyclic
                "modifications": [                  # Chemical modifications
                    {
                        "ccd": "SEP",               # CCD code for modification
                        "position": 15              # 1-based position to modify
                    }
                ]
                # Note: msa field would go here for proteins with MSA data
            },
            {
                "id": "B",
                "molecule_type": "dna",
                "sequence": "ATCGATCGATCG",
                "cyclic": False
            }
        ],

        # Optional: Ligands
        "ligands": [
            {
                "id": "L1",
                "smiles": "CC(=O)OC1=CC=CC=C1C(=O)O"  # Using SMILES
            },
            {
                "id": "L2",
                "ccd": "ATP"                        # Using CCD code instead
            }
        ],

        # Optional: Constraints
        "constraints": [
            {
                "constraint_type": "pocket",
                "binder": "L1",
                "contacts": [
                    {
                        "id": "A",
                        "residue_index": 25
                    }
                ]
            }
        ],

        # Optional: Prediction parameters (all with defaults shown)
        "recycling_steps": 3,                      # 1-10, controls model iterations
        "sampling_steps": 50,                      # 10-1000, diffusion sampling steps
        "diffusion_samples": 1,                    # 1-25, number of samples to generate
        "step_scale": 1.638,                       # 0.5-5.0, controls sampling step magnitude
        "without_potentials": False,               # Whether to include potentials
        "output_format": "mmcif",                  # Output format (currently only mmcif)
        "concatenate_msas": False,                 # Whether to concatenate MSAs
        "write_full_pae": True,                    # Include one full PAE matrix per diffusion sample
        "write_full_pde": True                     # Include one full PDE matrix per diffusion sample
    }

    print("Making comprehensive prediction request...")
    response = requests.post("http://localhost:8000/biology/mit/boltz2/predict",
                           headers=headers, data=json.dumps(data))

    if response.status_code == 200:
        result = response.json()
        print(f"Prediction completed successfully!")
        print(f"Number of structures returned: {len(result['structures'])}")
        print(f"Confidence scores: {result['confidence_scores']}")
        print(f"Complex PDE scores: {result.get('complex_pde_scores', [])}")
        print(f"Complex ipDE scores: {result.get('complex_ipde_scores', [])}")
        if "pae" in result:
            print(f"PAE matrix shape: {len(result['pae'])} x {len(result['pae'][0])} x {len(result['pae'][0][0])}")
        if "pde" in result:
            print(f"PDE matrix shape: {len(result['pde'])} x {len(result['pde'][0])} x {len(result['pde'][0][0])}")
        print(f"Runtime metrics: {result.get('metrics', {})}")

        # Access first structure
        if result['structures']:
            structure = result['structures'][0]
            print(f"Structure format: {structure['format']}")
            print(f"Structure length: {len(structure['structure'])} characters")
    else:
        print(f"Request failed with status {response.status_code}")
        print(response.text)
PAE and PDE Outputs#

The following example requests full PAE and PDE matrices and reads both aggregate PDE scores and optional full matrices from the response.

#!/bin/bash

JSON='{
"polymers": [
    {
    "id": "A",
    "molecule_type": "protein",
    "sequence": "MKTVRQERLKSIVRILERSKEPVSGAQLAEELSVSRQVIVQDIAYLRSLGYNIVATPRGYVLAGG"
    }
],
"diffusion_samples": 1,
"write_full_pae": true,
"write_full_pde": true
}'

response=$(curl -s -X POST \
-H "Content-Type: application/json" \
-d "$JSON" \
http://localhost:8000/biology/mit/boltz2/predict)

echo "Complex PDE score: $(echo "$response" | jq -r '.complex_pde_scores[0]')"
echo "Complex ipDE score: $(echo "$response" | jq -r '.complex_ipde_scores[0]')"
echo "PAE entries: $(echo "$response" | jq '.pae | length')"
echo "PDE entries: $(echo "$response" | jq '.pde | length')"
import requests
import json

if __name__ == "__main__":
    data = {
        "polymers": [
            {
                "id": "A",
                "molecule_type": "protein",
                "sequence": "MKTVRQERLKSIVRILERSKEPVSGAQLAEELSVSRQVIVQDIAYLRSLGYNIVATPRGYVLAGG",
            }
        ],
        "diffusion_samples": 1,
        "write_full_pae": True,
        "write_full_pde": True,
    }
    response = requests.post(
        "http://localhost:8000/biology/mit/boltz2/predict",
        headers={"content-type": "application/json"},
        data=json.dumps(data),
    )
    result = response.json()
    print(f"Complex PDE score: {result['complex_pde_scores'][0]}")
    print(f"Complex ipDE score: {result['complex_ipde_scores'][0]}")
    if "pae" in result:
        print(f"PAE matrix shape: {len(result['pae'])} x {len(result['pae'][0])} x {len(result['pae'][0][0])}")
    if "pde" in result:
        print(f"PDE matrix shape: {len(result['pde'])} x {len(result['pde'][0])} x {len(result['pde'][0][0])}")

Check Readiness#

Endpoint path: /v1/health/ready

Input parameters#

None.

Outputs#

The output of the endpoint is a JSON response with a value that indicates the readiness of the microservice. When the NIM is ready, it returns the response 200.

Example#

#!/bin/bash
URL=${NIM_URL:-"http://localhost:8000/v1/health/ready"}
curl -s -w "\nStatus code: %{http_code}\n" -H "Content-Type: application/json" $URL
import requests
import os

if __name__ == "__main__":
    url = os.environ.get("NIM_URL", "http://localhost:8000/v1/health/ready")
    headers = {
        "content-type": "application/json"
    }
    try:
        response = requests.get(url, headers=headers)
        print(f"NIM readiness check returned {response.status_code}")
        assert response.status_code == 200, f"Unexpected status code: {response.status_code}"
    except Exception as e:
        print(f"Health query failed: {e}")