Discover Models with the Python SDK and REST API

Explore the OCI Generative AI model catalog, filter models for your application, and inspect model metadata with signed Python SDK and REST requests.

Overview

The Model Discovery API brings model identifiers, capabilities, API compatibility, configuration parameters, and regional availability together. Use this metadata to choose models for a chat assistant, search application, or multimodal workflow before writing an inference request.

Applications can query the catalog to build model-selection interfaces and refresh the available choices. Regional lifecycle information helps you plan upgrades and evaluate reported replacements.

MetadataWhat it reportsHow to use it
Identity and providerModel identifiers and vendor information.Find a provider and copy an exact model ID.
Tasks and modalitiesModel capabilities and supported input-to-output types.Identify candidates for chat, embeddings, reranking, or multimodal workflows.
API compatibilityInference API capabilities reported for the model.Select an API that the model supports.
Regional availabilityRealms, regions, and on-demand or dedicated serving modes.Find candidates for your deployment location and serving mode.
Hosted or proxy accessThe model's access classification.Review the appropriate deployment and data-handling documentation.
ParametersReported names, types, ranges, defaults, and descriptions.Build configuration forms and check settings against returned metadata.
LifecycleDeprecation and retirement dates by region and serving mode, plus replacements when provided.Identify affected deployments and evaluate migration candidates.
FiltersModel ID, realm, region, capability, API capability, serving mode, access type, and lifecycle status.Build a focused model picker or automate catalog searches.

Individual models might not populate every field. Discovery helps you select and configure models. It doesn't invoke inference, grant access, or benchmark model quality. Validate a selected model and any suggested replacement with your application's workload.

In this tutorial, you authenticate with an OCI IAM session, query discovery with the Python SDK, send the same request over REST, and try ten practical examples. You also learn how to list models already imported into your compartment.

Note

The operation is ListModelDiscovery. It is distinct from list_models(), list_imported_models(), and the OpenAI-compatible /models endpoint. Sending a discovery request to Chicago doesn't restrict catalog results to Chicago. Use a region filter to select the catalog region.

Before You Begin

To complete this tutorial, you need:

  • An OCI account and a compartment where you can inspect models. ListModelDiscovery requires GENERATIVE_AI_MODEL_INSPECT. See Model Permissions.
  • For the imported-model examples, permission to inspect imported models in the selected compartment and an existing imported resource to find. See Imported Model Permissions.
  • Python 3.11 or later, uv, and an installed OCI CLI. The commands use a Bash-compatible shell on macOS, Linux, or Windows Subsystem for Linux.
  • A browser for OCI CLI session authentication and access to the service endpoint. These examples use US Midwest (Chicago) in the OC1 commercial realm.

The examples use OCI IAM request signing with a session token and its private key. An OCI Generative AI API key is a separate credential and doesn't replace the signer used here.

1. Prepare the Environment and Sign In

  1. Create and activate a Python virtual environment, then install the packages:
    uv venv --python 3.11 .venv
    source .venv/bin/activate
    uv pip install --upgrade oci requests

    If the environment already exists, activate it before installing or upgrading the packages.

  2. Check that the installed SDK provides the discovery method and that the OCI CLI is available:
    python -c 'import oci; print(oci.__version__); print(hasattr(oci.generative_ai.GenerativeAiClient, "list_model_discovery"))'
    oci --version

    The method check should print True. If it prints False, install an SDK version that includes list_model_discovery.

  3. Set the example profile, endpoint region, and compartment for this shell:
    export OCI_DISCOVERY_PROFILE=GENAI_DISCOVERY
    export OCI_DISCOVERY_REGION=us-chicago-1
    export OCI_COMPARTMENT_ID='<your-compartment-ocid>'

    Replace the compartment placeholder with a compartment you can access. GENAI_DISCOVERY is the example profile name. The scripts read these environment variables without using a default compartment.

  4. Create a CLI session and complete the browser sign-in:
    oci session authenticate \
      --profile-name "$OCI_DISCOVERY_PROFILE" \
      --region "$OCI_DISCOVERY_REGION"

    The CLI stores the profile in ~/.oci/config. The Python examples load the profile's session token and private key with SecurityTokenSigner. When the session expires, authenticate again and restart the script or interactive Python session. See Token-based Authentication for the CLI.

2. Query Discovery with the Python SDK

This example finds current on-demand chat models in the selected region. It follows pagination and prints model IDs, vendors, and capabilities. The helper functions are reused in later examples.

  1. Save the following code as model_discovery.py in your working directory:
    """Query OCI Generative AI model metadata with an IAM session."""
    
    import os
    import re
    import unicodedata
    from pathlib import Path
    
    import oci
    
    
    def load_session():
        config = oci.config.from_file(
            profile_name=os.environ["OCI_DISCOVERY_PROFILE"]
        )
        config["region"] = os.environ["OCI_DISCOVERY_REGION"]
        token = Path(config["security_token_file"]).expanduser().read_text(
            encoding="utf-8"
        ).strip()
        private_key = oci.signer.load_private_key_from_file(
            str(Path(config["key_file"]).expanduser()),
            config.get("pass_phrase"),
        )
        signer = oci.auth.signers.SecurityTokenSigner(token, private_key)
        return config, signer
    
    
    def make_client():
        config, signer = load_session()
        return oci.generative_ai.GenerativeAiClient(config, signer=signer)
    
    
    def fetch_models(client, compartment_id, source="discovery", **filters):
        if source not in {"discovery", "imported"}:
            raise ValueError("source must be discovery or imported")
        operation = (
            client.list_model_discovery
            if source == "discovery"
            else client.list_imported_models
        )
        items = []
        while True:
            response = operation(compartment_id=compartment_id, **filters)
            items.extend(oci.util.to_dict(response.data)["items"])
            next_page = response.headers.get("opc-next-page")
            if not next_page:
                return items
            filters["page"] = next_page
    
    
    def normalized(value):
        value = unicodedata.normalize("NFKC", str(value)).casefold()
        return " ".join(re.findall(r"[^\W\d_]+|\d+", value))
    
    
    def search_models(models, query):
        terms = [normalized(term) for term in query.split()]
        terms = [term for term in terms if term]
    
        def matches(model):
            values = [
                model.get(field)
                for field in ("model_id", "id", "display_name", "vendor", "version")
            ]
            values.extend(
                value for value in (model.get("data_source") or {}).values()
                if isinstance(value, str)
            )
            candidates = [normalized(value) for value in values if value]
            return all(
                any(
                    re.search(
                        (r"(?<!\d)" if term[0].isdigit() else "")
                        + re.escape(term),
                        value,
                    )
                    for value in candidates
                )
                for term in terms
            )
    
        return [model for model in models if matches(model)]
    
    
    def show(models):
        print(f"{len(models)} matching model(s)")
        for model in models:
            name = (
                model.get("model_id")
                or model.get("display_name")
                or model.get("id")
            )
            capabilities = ", ".join(model.get("capabilities") or [])
            vendor = model.get("vendor") or "not reported"
            print(f"{name} | vendor={vendor} | capabilities={capabilities}")
    
    
    if __name__ == "__main__":
        client = make_client()
        models = fetch_models(
            client,
            os.environ["OCI_COMPARTMENT_ID"],
            region=[os.environ["OCI_DISCOVERY_REGION"]],
            capability=["CHAT"],
            serving_mode=["ON_DEMAND"],
            is_deprecated=False,
            is_on_demand_retired=False,
        )
        show(models)
  2. Run the script in the activated environment:
    python model_discovery.py
  3. Review the matching models and copy an identifier you want to inspect.

    Results depend on the current catalog and your filters. An empty result isn't an authentication test or proof that a model can't be imported.

    The SDK derives its management endpoint from config["region"]. The separate region=[...] argument filters catalog availability. Neither value configures an inference endpoint.

    For method details, see GenerativeAiClient.list_model_discovery.

You have a paginated discovery query and reusable functions for local searching and imported-model inventory.

3. Call the Discovery REST API Directly

A direct request exposes the HTTP method, path, query parameters, and response headers. It calls the same discovery operation as the SDK. The request still requires OCI IAM authorization.

GET /20231130/modelDiscovery?compartmentId=<your-compartment-ocid>
  1. Set the public Chicago management endpoint:
    export OCI_DISCOVERY_ENDPOINT=https://generativeai.us-chicago-1.oci.oraclecloud.com

    This example uses the OC1 Chicago endpoint. For another region or realm, use the appropriate management endpoint in the API reference and update OCI_DISCOVERY_REGION for the catalog filter. Discovery uses the management API, not a chat inference URL.

  2. Send a signed request with the OCI CLI:
    oci raw-request \
      --profile "$OCI_DISCOVERY_PROFILE" \
      --auth security_token \
      --http-method GET \
      --target-uri "${OCI_DISCOVERY_ENDPOINT}/20231130/modelDiscovery?compartmentId=${OCI_COMPARTMENT_ID}&region=${OCI_DISCOVERY_REGION}&capability=CHAT&servingMode=ON_DEMAND&isDeprecated=false&isOnDemandRetired=false"

    The CLI signs the request and displays the response. To retrieve another page, pass the returned opc-next-page value as the URL-encoded page query parameter. The Python example below follows pagination automatically.

  3. Save the following code as model_discovery_rest.py alongside model_discovery.py:

    The script imports the session-signing helper from the SDK example. It sends HTTP requests through requests and prints status and request IDs to standard error.

    """Call Model Discovery over HTTP with OCI request signing."""
    
    import os
    import sys
    
    import requests
    
    from model_discovery import load_session
    
    
    def fetch_rest_models(signer, endpoint, compartment_id, region):
        url = endpoint.rstrip("/") + "/20231130/modelDiscovery"
        params = {
            "compartmentId": compartment_id,
            "region": [region],
            "capability": ["CHAT"],
            "servingMode": ["ON_DEMAND"],
            "isDeprecated": "false",
            "isOnDemandRetired": "false",
        }
        items = []
        while True:
            response = requests.get(
                url,
                params=params,
                headers={"Accept": "application/json"},
                auth=signer,
                timeout=(10, 30),
                allow_redirects=False,
            )
            request_id = response.headers.get("opc-request-id", "not reported")
            print(
                f"HTTP {response.status_code} | request ID: {request_id}",
                file=sys.stderr,
            )
            if 300 <= response.status_code < 400:
                raise RuntimeError("Check the service endpoint. Redirect not followed.")
            response.raise_for_status()
            items.extend(response.json()["items"])
            next_page = response.headers.get("opc-next-page")
            if not next_page:
                return items
            params["page"] = next_page
    
    
    if __name__ == "__main__":
        _, signer = load_session()
        models = fetch_rest_models(
            signer,
            os.environ["OCI_DISCOVERY_ENDPOINT"],
            os.environ["OCI_COMPARTMENT_ID"],
            os.environ["OCI_DISCOVERY_REGION"],
        )
        print(f"{len(models)} matching model(s)")
        for model in models:
            print(f"{model['modelId']} | vendor={model.get('vendor') or 'not reported'}")
  4. Run the REST example:
    python model_discovery_rest.py
  5. Compare the model identifiers with the SDK results.

    Use the same profile, compartment, endpoint region, and filters for the comparison. Results can change as the catalog changes.

    REST response fields use camelCase, such as modelId. The SDK's oci.util.to_dict() output uses snake_case, such as model_id. For list-valued REST filters, repeat the query key, for example region=us-chicago-1&region=us-ashburn-1. The requests package encodes lists in params this way.

4. Prepare the Practical Examples

  1. Save this setup as model_discovery_recipes.py alongside model_discovery.py:
    import json
    import os
    from collections import Counter
    from pathlib import Path
    
    from model_discovery import fetch_models, make_client, search_models, show
    
    client = make_client()
    compartment_id = os.environ["OCI_COMPARTMENT_ID"]
    region = os.environ["OCI_DISCOVERY_REGION"]
    
    
    def fetch(source="discovery", **filters):
        return fetch_models(client, compartment_id, source=source, **filters)
  2. Start Python with the setup loaded:
    python -i model_discovery_recipes.py

    The -i option keeps Python open. Run any example in the following sections in that session. Alternatively, append an example to the setup file and run it without -i.

Choose Filters from the Returned Catalog

Before selecting filters, inspect the values reported for the models in your region:

catalog = fetch(region=[region])
for field in ("capabilities", "api_capability", "model_access", "vendor"):
    counts = Counter()
    for model in catalog:
        values = model.get(field) or []
        if isinstance(values, str):
            values = [values]
        counts.update(set(values))
    print(field, dict(sorted(counts.items())))

for field in ("realm", "region", "serving_modes"):
    counts = Counter()
    for model in catalog:
        values = set()
        for availability in model.get("availability") or []:
            value = availability.get(field)
            if isinstance(value, list):
                values.update(value)
            elif value:
                values.add(value)
        counts.update(values)
    print(field, dict(sorted(counts.items())))

Each count is the number of returned model entries associated with a value. A model can contribute to several capability or availability values. Counts reflect the query filters.

SDK argumentExample or purpose
region, realmLists of region identifiers or realm values. Use values reported by the catalog.
model_idOne exact model identifier.
capabilityA list such as ["CHAT"], ["TEXT_EMBEDDINGS"], or ["TEXT_RERANK"].
api_capabilityA list of API identifiers reported by the catalog.
serving_mode["ON_DEMAND"] or ["DEDICATED"].
model_access["HOSTED"] or ["PROXY"].
is_deprecatedFalse excludes deprecated models.
is_on_demand_retired, is_dedicated_retiredUse False for the serving mode whose retired models you want to exclude.
page, limitPagination token and requested page size. The example follows opc-next-page until all pages are retrieved.

Use search_models() for local substring matching after fetching all pages. Search isn't a parameter of ListModelDiscovery. Review the operation reference for the full filter vocabulary.

Ten Practical Python Examples

1. Browse Models Across Both Serving Modes

Filter by your intended deployment region. Omitting serving_mode leaves both on-demand and dedicated models eligible.

catalog = fetch(region=[region])
show(catalog)

2. Find Models by Partial Name or Vendor

Search the returned metadata locally. Every space-separated term must match a searchable field. Matching ignores case and punctuation. The service's model_id filter requires an exact identifier.

catalog = fetch(region=[region])
show(search_models(catalog, "grok"))

Change the search term to a provider or model family in your results.

3. Find Current On-Demand Chat Models

Exclude deprecated models and models retired for on-demand serving.

show(fetch(
    region=[region],
    capability=["CHAT"],
    serving_mode=["ON_DEMAND"],
    is_deprecated=False,
    is_on_demand_retired=False,
))

4. Find Models for Dedicated Serving

Apply the retirement filter for dedicated serving. This query searches the discovery catalog. To list resources you have imported, use example 8.

show(fetch(
    region=[region],
    serving_mode=["DEDICATED"],
    is_deprecated=False,
    is_dedicated_retired=False,
))

5. Find Embedding and Reranking Models

Query each capability separately. Discovery uses TEXT_EMBEDDINGS and TEXT_RERANK. Imported-model resources use a different capability vocabulary, including EMBEDDING and RERANK.

for capability in ("TEXT_EMBEDDINGS", "TEXT_RERANK"):
    print("\nCapability:", capability)
    show(fetch(region=[region], capability=[capability]))

6. Find Proxy Models with a Reported API Capability

Read the API identifiers returned by the catalog, then copy a value to filter the proxy models. Review external-call notes and data handling for a selected model.

catalog = fetch(region=[region], model_access=["PROXY"])
api_values = sorted({
    api for model in catalog for api in model.get("api_capability") or []
})
print(api_values)
if api_values:
    api = input("Enter an API capability from the list: ").strip()
    if api in api_values:
        show(fetch(
            region=[region],
            model_access=["PROXY"],
            api_capability=[api],
        ))
    else:
        print("Enter an exact value from the returned list.")
else:
    print("No API capabilities were reported for these results.")

7. Inspect Model Parameters and Lifecycle Details

Copy an exact model identifier from your results. Inspect the complete availability entries to keep each region associated with its serving modes, lifecycle dates, and reported replacements.

model_id = input("Enter an exact model ID from your results: ").strip()
for model in fetch(model_id=model_id):
    print(model["model_id"])
    for field in (
        "api_capability", "modality_support", "parameters", "availability"
    ):
        print(field)
        print(json.dumps(model.get(field), indent=2, default=str))

A field can be empty or unreported. Evaluate any suggested replacement with your application before migrating.

8. Find an Imported Qwen Model

Use source="imported" to query existing imports in your compartment and client region. This example searches for a Qwen 3.6 35B model. It returns a match only if you have imported a matching model and can inspect that resource. See Compatible Qwen 3.6 Models for import compatibility.

imports = fetch(source="imported")
selected = search_models(imports, "qwen 3.6 35B")
show(selected)
for model in selected:
    print("Imported resource OCID:", model["id"])
    print("Resource state:", model.get("lifecycle_state"))

The returned OCID identifies the imported model resource. An ACTIVE resource state doesn't establish that an inference endpoint has been deployed. For deployment steps, see Managing Imported Models.

9. Compare Discovery with Imported Resources

Compare the two sources when a model appears in imported inventory but not in discovery. Discovery results and the resources imported into your compartment answer different questions.

print("Discovery catalog for the selected region:")
show(search_models(fetch(region=[region]), "qwen"))
print("\nImported resources in this compartment and client region:")
show(search_models(fetch(source="imported"), "qwen"))

If imported results are empty, check the compartment, client region, and permissions. The imported-model query doesn't scan other compartments or regions.

10. Export Matching Imported Models

Write the full metadata for selected imported resources to a local JSON file.

selected = search_models(fetch(source="imported"), "qwen 3.6 35B")
Path("qwen-imports.json").write_text(
    json.dumps({"items": selected}, indent=2, default=str),
    encoding="utf-8",
)
print(f"Saved {len(selected)} matching resource(s) to qwen-imports.json")

Verify the Result and Troubleshoot

A successful request returns model metadata, or an empty list when no entries match. Review identifiers, capabilities, parameters, and availability before building an inference request.

SymptomWhat to check
The SDK has no list_model_discovery method.Upgrade oci in the active virtual environment and repeat the method check.
A signed request returns HTTP 401.Validate or recreate the CLI session, check the selected profile, and restart Python so it reads the new token.
The service reports an authorization error.Check IAM permissions and the compartment OCID. Imported inventory requires permission to inspect imported-model resources.
An exact model-ID filter returns no results.Browse or search locally first, then copy an identifier from the response. Check the region and lifecycle filters.
The response includes availability outside the endpoint region.Set the discovery region filter explicitly and inspect each availability entry. The endpoint region alone doesn't constrain catalog results.
An imported model isn't returned.Check the client's region, compartment, permissions, and resource display name. Use list_imported_models() for imported inventory.
Only part of the catalog is returned.Follow opc-next-page. The Python examples on this page retrieve all pages.
The REST request fails while the SDK succeeds.Use the same profile and compartment. Check the management endpoint, path, and camelCase query names, then review the status and request ID.

For the generated CLI command, see list-model-discovery. For imported resources, see ListImportedModels. For the metadata fields returned by discovery, see ModelDiscovery.