Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Azure AI Search SDK for Python. Use for vector search, hybrid search, semantic ranking, indexing, and skillsets. Triggers: "azure-search-documents", "SearchClient", "SearchIndexClient", "vector search", "hybrid search", "semantic search".
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 158% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 384% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 299% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 198% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 183% | 0% |
Full-text, vector, and hybrid search with AI enrichment capabilities.
bashpip install azure-search-documents
bashAZURE_SEARCH_ENDPOINT=https://<service-name>.search.windows.net # Required for all auth methods AZURE_SEARCH_INDEX_NAME=<your-index-name> # Required for all auth methods AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production AZURE_SEARCH_API_KEY=<your-api-key> # Only required for the legacy API-key auth path below
> 🔑 Two rules apply to every code sample below: > > 1. Prefer DefaultAzureCredential. It works locally (Azure CLI / VS Code / Developer CLI) and in Azure (managed identity, workload identity) with no code change. Avoid connection strings, account/API keys — they bypass Entra audit and rotation. > - Local dev: DefaultAzureCredential works as-is. > - Production: set AZURE_TOKEN_CREDENTIALS=prod (or AZURE_TOKEN_CREDENTIALS=<specific_credential>) to constrain the credential chain to production-safe credentials. > 2. Wrap every client in a context manager so HTTP transports, sockets, and token caches are released deterministically: > - Sync: with <Client>(...) as client: > - Async: async with <Client>(...) as client: and async with DefaultAzureCredential() as credential: (from azure.identity.aio) > > Snippets may abbreviate this setup, but production code should always follow both rules.
pythonimport os from azure.identity import DefaultAzureCredential, ManagedIdentityCredential from azure.search.documents import SearchClient # Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential> credential = DefaultAzureCredential(require_envvar=True) # Or use a specific credential directly in production: # See https://learn.microsoft.com/python/api/overview/azure/identity-readme?view=azure-python#credential-classes # credential = ManagedIdentityCredential() with SearchClient( endpoint=os.environ["AZURE_SEARCH_ENDPOINT"], index_name=os.environ["AZURE_SEARCH_INDEX_NAME"], credential=credential, ) as client: results = list(client.search(search_text="*", top=5))
New code should use DefaultAzureCredential above. Use AzureKeyCredential only if you have an existing keyed deployment that hasn't been migrated to Entra ID yet — for example, regulated environments still completing their Entra rollout. The same AzureKeyCredential works with SearchIndexClient and SearchIndexerClient for admin operations.
pythonimport os from azure.core.credentials import AzureKeyCredential from azure.search.documents import SearchClient with SearchClient( endpoint=os.environ["AZURE_SEARCH_ENDPOINT"], index_name=os.environ["AZURE_SEARCH_INDEX_NAME"], credential=AzureKeyCredential(os.environ["AZURE_SEARCH_API_KEY"]), ) as client: results = list(client.search(search_text="*", top=5))
| Client | Purpose | |--------|---------| | SearchClient | Search and document operations | | SearchIndexClient | Index management, synonym maps | | SearchIndexerClient | Indexers, data sources, skillsets |
pythonfrom azure.search.documents.indexes import SearchIndexClient from azure.search.documents.indexes.models import ( SearchIndex, SearchField, SearchFieldDataType, VectorSearch, HnswAlgorithmConfiguration, VectorSearchProfile, SearchableField, SimpleField ) fields = [ SimpleField(name="id", type=SearchFieldDataType.String, key=True), SearchableField(name="title", type=SearchFieldDataType.String), SearchableField(name="content", type=SearchFieldDataType.String), SearchField( name="content_vector", type=SearchFieldDataType.Collection(SearchFieldDataType.Single), searchable=True, vector_search_dimensions=1536, vector_search_profile_name="my-vector-profile" ) ] vector_search = VectorSearch( algorithms=[ HnswAlgorithmConfiguration(name="my-hnsw") ], profiles=[ VectorSearchProfile( name="my-vector-profile", algorithm_configuration_name="my-hnsw" ) ] ) index = SearchIndex( name="my-index", fields=fields, vector_search=vector_search ) with SearchIndexClient(endpoint, DefaultAzureCredential()) as index_client: index_client.create_or_update_index(index)
pythonfrom azure.search.documents import SearchClient documents = [ { "id": "1", "title": "Azure AI Search", "content": "Full-text and vector search service", "content_vector": [0.1, 0.2, ...] # 1536 dimensions } ] with SearchClient(endpoint, "my-index", DefaultAzureCredential()) as client: result = client.upload_documents(documents) print(f"Uploaded {len(result)} documents")
pythonresults = client.search( search_text="azure search", select=["id", "title", "content"], top=10 ) for result in results: print(f"{result['title']}: {result['@search.score']}")
pythonfrom azure.search.documents.models import VectorizedQuery # Your query embedding (1536 dimensions) query_vector = get_embedding("semantic search capabilities") vector_query = VectorizedQuery( vector=query_vector, k_nearest_neighbors=10, fields="content_vector" ) results = client.search( vector_queries=[vector_query], select=["id", "title", "content"] ) for result in results: print(f"{result['title']}: {result['@search.score']}")
pythonfrom azure.search.documents.models import VectorizedQuery vector_query = VectorizedQuery( vector=query_vector, k_nearest_neighbors=10, fields="content_vector" ) results = client.search( search_text="azure search", vector_queries=[vector_query], select=["id", "title", "content"], top=10 )
pythonfrom azure.search.documents.models import QueryType results = client.search( search_text="what is azure search", query_type=QueryType.SEMANTIC, semantic_configuration_name="my-semantic-config", select=["id", "title", "content"], top=10 ) for result in results: print(f"{result['title']}") if result.get("@search.captions"): print(f" Caption: {result['@search.captions'][0].text}")
pythonresults = client.search( search_text="*", filter="category eq 'Technology' and rating gt 4", order_by=["rating desc"], select=["id", "title", "category", "rating"] )
pythonresults = client.search( search_text="*", facets=["category,count:10", "rating"], top=0 # Only get facets, no documents ) for facet_name, facet_values in results.get_facets().items(): print(f"{facet_name}:") for facet in facet_values: print(f" {facet['value']}: {facet['count']}")
python# Autocomplete results = client.autocomplete( search_text="sea", suggester_name="my-suggester", mode="twoTerms" ) # Suggest results = client.suggest( search_text="sea", suggester_name="my-suggester", select=["title"] )
pythonfrom azure.search.documents.indexes import SearchIndexerClient from azure.search.documents.indexes.models import ( SearchIndexer, SearchIndexerDataSourceConnection, SearchIndexerSkillset, EntityRecognitionSkill, InputFieldMappingEntry, OutputFieldMappingEntry ) with SearchIndexerClient(endpoint, DefaultAzureCredential()) as indexer_client: # Use managed identity (search service must have RBAC role on the storage account). Avoid storage connection strings with embedded keys. data_source = SearchIndexerDataSourceConnection( name="my-datasource", type="azureblob", connection_string="ResourceId=/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/<acct>", container={"name": "documents"} ) indexer_client.create_or_update_data_source_connection(data_source) # Create skillset skillset = SearchIndexerSkillset( name="my-skillset", skills=[ EntityRecognitionSkill( inputs=[InputFieldMappingEntry(name="text", source="/document/content")], outputs=[OutputFieldMappingEntry(name="organizations", target_name="organizations")] ) ] ) indexer_client.create_or_update_skillset(skillset) # Create indexer indexer = SearchIndexer( name="my-indexer", data_source_name="my-datasource", target_index_name="my-index", skillset_name="my-skillset" ) indexer_client.create_or_update_indexer(indexer)
azure.xxx sync clients with azure.xxx.aio async clients in the same call path. Choose one mode per module.with Client(...) as client: (sync) or async with Client(...) as client: (async). For async DefaultAzureCredential from azure.identity.aio, also use async with credential: so tokens and transports are cleaned up.| File | Contents | |------|----------| | references/vector-search.md | HNSW configuration, integrated vectorization, multi-vector queries | | references/semantic-ranking.md | Semantic configuration, captions, answers, hybrid patterns | | scripts/setup_vector_index.py | CLI script to create vector-enabled search index |
Write clean, idiomatic Python code for Azure AI Search using azure-search-documents.
bashpip install azure-search-documents azure-identity
bashAZURE_SEARCH_ENDPOINT=https://<search-service>.search.windows.net # Required for all auth methods AZURE_SEARCH_INDEX_NAME=<index-name> # Required for all auth methods AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
pythonimport os from azure.identity import DefaultAzureCredential, ManagedIdentityCredential from azure.search.documents import SearchClient # Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential> credential = DefaultAzureCredential(require_envvar=True) # Or use a specific credential directly in production: # See https://learn.microsoft.com/python/api/overview/azure/identity-readme?view=azure-python#credential-classes # credential = ManagedIdentityCredential() with SearchClient( endpoint=os.environ["AZURE_SEARCH_ENDPOINT"], index_name=os.environ["AZURE_SEARCH_INDEX_NAME"], credential=credential, ) as client: results = list(client.search(search_text="*", top=5))
| Client | Purpose | |--------|---------| | SearchClient | Query indexes, upload/update/delete documents | | SearchIndexClient | Create/manage indexes, knowledge sources, knowledge bases | | SearchIndexerClient | Manage indexers, skillsets, data sources | | KnowledgeBaseRetrievalClient | Agentic retrieval with LLM-powered Q&A |
pythonfrom azure.search.documents.indexes import SearchIndexClient from azure.search.documents.indexes.models import ( SearchIndex, SearchField, VectorSearch, VectorSearchProfile, HnswAlgorithmConfiguration, AzureOpenAIVectorizer, AzureOpenAIVectorizerParameters, SemanticSearch, SemanticConfiguration, SemanticPrioritizedFields, SemanticField ) index = SearchIndex( name=index_name, fields=[ SearchField(name="id", type="Edm.String", key=True), SearchField(name="content", type="Edm.String", searchable=True), SearchField(name="embedding", type="Collection(Edm.Single)", vector_search_dimensions=3072, vector_search_profile_name="vector-profile"), ], vector_search=VectorSearch( profiles=[VectorSearchProfile( name="vector-profile", algorithm_configuration_name="hnsw-algo", vectorizer_name="openai-vectorizer" )], algorithms=[HnswAlgorithmConfiguration(name="hnsw-algo")], vectorizers=[AzureOpenAIVectorizer( vectorizer_name="openai-vectorizer", parameters=AzureOpenAIVectorizerParameters( resource_url=aoai_endpoint, deployment_name=embedding_deployment, model_name=embedding_model ) )] ), semantic_search=SemanticSearch( default_configuration_name="semantic-config", configurations=[SemanticConfiguration( name="semantic-config", prioritized_fields=SemanticPrioritizedFields( content_fields=[SemanticField(field_name="content")] ) )] ) ) with SearchIndexClient(endpoint, credential) as index_client: index_client.create_or_update_index(index)
pythonfrom azure.search.documents import SearchIndexingBufferedSender # Batch upload with automatic batching with SearchIndexingBufferedSender(endpoint, index_name, credential) as sender: sender.upload_documents(documents) # Direct operations via SearchClient with SearchClient(endpoint, index_name, credential) as search_client: search_client.upload_documents(documents) # Add new search_client.merge_documents(documents) # Update existing search_client.merge_or_upload_documents(documents) # Upsert search_client.delete_documents(documents) # Remove
python# Basic search results = search_client.search(search_text="query") # Vector search from azure.search.documents.models import VectorizedQuery results = search_client.search( search_text=None, vector_queries=[VectorizedQuery( vector=embedding, k_nearest_neighbors=5, fields="embedding" )] ) # Hybrid search (vector + keyword) results = search_client.search( search_text="query", vector_queries=[VectorizedQuery(vector=embedding, k_nearest_neighbors=5, fields="embedding")], query_type="semantic", semantic_configuration_name="semantic-config" ) # With filters results = search_client.search( search_text="query", filter="category eq 'technology'", select=["id", "title", "content"], top=10 )
For LLM-powered Q&A with answer synthesis, see references/agentic-retrieval.md.
Key concepts:
EXTRACTIVE_DATA (raw chunks) or ANSWER_SYNTHESIS (LLM-generated answers)pythonfrom azure.search.documents.aio import SearchClient async with SearchClient(endpoint, index_name, credential) as client: results = await client.search(search_text="query") async for result in results: print(result["title"])
DefaultAzureCredential for code that runs locally (instead of API keys). Use a specific token credential for code that runs in Azure.SearchIndexingBufferedSender for batch uploads (handles batching/retries)create_or_update_index for idempotent index creationclose()| EDM Type | Python | Notes | |----------|--------|-------| | Edm.String | str | Searchable text | | Edm.Int32 | int | Integer | | Edm.Int64 | int | Long integer | | Edm.Double | float | Floating point | | Edm.Boolean | bool | True/False | | Edm.DateTimeOffset | datetime | ISO 8601 | | Collection(Edm.Single) | Listfloat] | Vector embeddings | | Collection(Edm.String) | Liststr] | String arrays |
pythonfrom azure.core.exceptions import ( HttpResponseError, ResourceNotFoundError, ResourceExistsError ) try: result = search_client.get_document(key="123") except ResourceNotFoundError: print("Document not found") except HttpResponseError as e: print(f"Search error: {e.message}")
Other measured skills in the registry, with their headline benchmark lift.