Fixes at a Glance
- HTTP 429 Rate Limit — use exponential backoff with a
SemaphoreSlimbatch limiter and add a distributed cache in front of Search to reduce effective QPS - Index Not Found — verify the index name exactly matches the Azure portal value (case-sensitive) and confirm the index has been fully provisioned before querying
- Field Mapping Mismatch — align your
SearchDocumentfield names and types with the index schema; check vector field dimensions match your embedding model’s output
Error 1: HTTP 429 — Request Rate Too Large
What You See
Azure.RequestFailedException: Service request failed.
Status: 429 (Too Many Requests)
Headers:
Retry-After: 2
Or in your application logs:
RequestFailedException: The request rate is too large.
Please try again later or consider scaling up your service.
Why It Happens
Every Azure AI Search tier has a queries-per-second (QPS) ceiling:
| Tier | Approximate QPS | Indexing Throughput |
|---|---|---|
| Free | 3 | Very limited |
| Basic | 15 | ~300 docs/sec |
| Standard S1 | 50 | ~1,500 docs/sec |
| Standard S2 | 100+ | ~3,000 docs/sec |
The 429 fires when your cumulative request rate (queries + indexing + management operations) exceeds these limits.
The Fix
Step 1: Configure retry policies (handles transient spikes)
using Azure;
using Azure.Search.Documents;
var options = new SearchClientOptions
{
Retry =
{
MaxRetries = 5,
Mode = RetryMode.Exponential,
Delay = TimeSpan.FromSeconds(1),
MaxDelay = TimeSpan.FromSeconds(30),
NetworkTimeout = TimeSpan.FromSeconds(60)
}
};
var searchClient = new SearchClient(
new Uri(endpoint),
"my-index",
new AzureKeyCredential(apiKey),
options);
The SDK respects the Retry-After header. Exponential backoff starts at 1 second, doubles each retry, caps at 30 seconds.
Step 2: Batch indexing operations
The biggest QPS consumer is usually indexing. Instead of one document per request, batch them:
var indexClient = searchClient.GetSearchIndexClient();
var batch = IndexDocumentsBatch.Create<SearchDocument>();
foreach (var document in documents)
{
batch.Actions.Add(IndexDocumentsAction.Upload(document));
// Flush every 1,000 documents
if (batch.Actions.Count >= 1000)
{
await searchClient.IndexDocumentsAsync(batch);
batch = IndexDocumentsBatch.Create<SearchDocument>();
await Task.Delay(500); // Brief pause between batches
}
}
// Flush remaining
if (batch.Actions.Count > 0)
await searchClient.IndexDocumentsAsync(batch);
Step 3: Cache repeated queries
using Microsoft.Extensions.Caching.Distributed;
public class CachedSearchService
{
private readonly SearchClient _searchClient;
private readonly IDistributedCache _cache;
public CachedSearchService(SearchClient searchClient, IDistributedCache cache)
{
_searchClient = searchClient;
_cache = cache;
}
public async Task<SearchResults<T>> SearchAsync<T>(string query, SearchOptions options)
{
// Cache key based on query and options
var cacheKey = $"search:{query}:{options.Filter}:{options.Top}";
var cached = await _cache.GetStringAsync(cacheKey);
if (cached is not null)
return JsonSerializer.Deserialize<SearchResults<T>>(cached)!;
var results = await _searchClient.SearchAsync<T>(query, options);
// Cache for 5 minutes
await _cache.SetStringAsync(cacheKey,
JsonSerializer.Serialize(results.Value),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
});
return results;
}
}
Error 2: Index Not Found
What You See
Azure.RequestFailedException: Service request failed.
Status: 404 (Not Found)
No index with the name 'my-index' was found in the search service.
Why It Happens
Three common causes:
- Typo in the index name — Index names are case-sensitive
- Wrong endpoint — Your code points to a different Azure AI Search instance
- Race condition — Index creation is async; your query runs before creation finishes
The Fix
Verify the index exists before querying:
using Azure.Search.Documents.Indexes;
var indexClient = new SearchIndexClient(
new Uri(endpoint),
new AzureKeyCredential(apiKey));
try
{
var index = await indexClient.GetIndexAsync("my-index");
Console.WriteLine($"Index '{index.Value.Name}' exists with {index.Value.Fields.Count} fields");
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
Console.WriteLine("Index does not exist. Creating...");
// Create the index here
}
Handle creation timing:
// After creating the index, verify it's ready
await indexClient.CreateOrUpdateIndexAsync(indexDefinition);
// Poll until index is available (usually under 5 seconds)
var ready = false;
for (var attempt = 0; attempt < 10 && !ready; attempt++)
{
try
{
await indexClient.GetIndexAsync(indexDefinition.Name);
ready = true;
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
await Task.Delay(1000);
}
}
if (!ready)
throw new InvalidOperationException($"Index '{indexDefinition.Name}' was not ready after 10 seconds");
Error 3: Field Mapping Mismatch
What You See
Azure.RequestFailedException: Service request failed.
Status: 400 (Bad Request)
The property 'ContentVector' does not exist on type 'search.document'.
Make sure to only use property names that are defined by the type.
Or during indexing:
A document in the batch could not be indexed.
Key: 'doc-001' Error: 'Field 'content_vector' was not found in the index schema.'
Why It Happens
The field names in your C# model don’t match the field names defined in the Azure AI Search index. Common mismatches:
| C# Property | Index Field | Problem |
|---|---|---|
ContentVector | content_vector | Casing/naming convention |
Id | id | C# uses PascalCase, index uses camelCase |
Embedding | (missing) | Field exists in C# but not in index |
The Fix
Use JsonPropertyName or SearchableField attributes to match field names:
using System.Text.Json.Serialization;
using Azure.Search.Documents.Indexes;
public class SearchDocument
{
[SimpleField(IsKey = true, IsFilterable = true)]
[JsonPropertyName("id")]
public string Id { get; set; } = string.Empty;
[SearchableField]
[JsonPropertyName("content")]
public string Content { get; set; } = string.Empty;
[SimpleField(IsFilterable = true)]
[JsonPropertyName("category")]
public string Category { get; set; } = string.Empty;
[VectorSearchField(VectorSearchDimensions = 1536, VectorSearchProfileName = "default-profile")]
[JsonPropertyName("content_vector")]
public IReadOnlyList<float>? ContentVector { get; set; }
}
Validate your model against the index schema:
var index = await indexClient.GetIndexAsync("my-index");
var indexFields = index.Value.Fields.Select(f => f.Name).ToHashSet();
// Check that all required fields exist
var requiredFields = new[] { "id", "content", "category", "content_vector" };
var missing = requiredFields.Where(f => !indexFields.Contains(f));
if (missing.Any())
{
throw new InvalidOperationException(
$"Index is missing fields: {string.Join(", ", missing)}");
}
Prevention Checklist
- Pin your index name in configuration, not inline strings —
IConfiguration["Search:IndexName"] - Log the SearchClient endpoint on startup so you can verify which service you’re connected to
- Set retry policies on every
SearchClientOptionsinstance - Use
JsonPropertyNameattributes on all C# model properties to make field mapping explicit - Monitor QPS in Azure Portal → Metrics → Search Requests per Second
Related Articles
- Azure Search Documents 11.7.0 Released — Latest SDK features
- Semantic Kernel Memory and Vector Stores — Using Azure AI Search with SK
- Fix 429 Rate Limit Exceeded Azure OpenAI — Similar throttling patterns in Azure OpenAI
- Build a Semantic Search API in .NET with Azure AI Search — Complete end-to-end implementation with indexing and querying