# Microsoft REST API Guidelines ## Introduction The Microsoft REST API Guidelines is a comprehensive documentation repository that provides prescriptive guidance for building RESTful APIs across Microsoft services. It establishes consistent patterns, naming conventions, and best practices that ensure APIs are intuitive, maintainable, and developer-friendly. The guidelines cover both Azure data plane services and Microsoft Graph APIs, offering detailed specifications for HTTP methods, URL structures, error handling, versioning, pagination, long-running operations, and more. This document serves as the authoritative reference for API design decisions at Microsoft and is published to help the broader API community adopt similar standards. The repository contains two main branches of guidelines: Azure REST API Guidelines (for Azure service teams building data plane APIs) and Microsoft Graph REST API Guidelines (for teams building Microsoft Graph services). Both follow REST/HTTP/JSON standards while providing service-specific adaptations. The guidelines promote API-first design, emphasizing developer experience through consistent naming, proper error responses, and well-documented contracts. Technology teams use these guidelines to ensure their APIs work seamlessly with SDKs across multiple programming languages while maintaining backward compatibility over time. --- ## HTTP Methods and CRUD Operations Standard HTTP methods for resource manipulation following REST conventions. ```bash # GET - Read a resource curl -X GET "https://api.contoso.com/v1.0/users/user@contoso.com" \ -H "Authorization: Bearer {token}" \ -H "Accept: application/json" # Response: 200 OK # { # "id": "user@contoso.com", # "displayName": "John Doe", # "email": "john.doe@contoso.com" # } # POST - Create a new resource (server-generated ID) curl -X POST "https://api.contoso.com/v1.0/users" \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/json" \ -d '{ "displayName": "Jane Smith", "email": "jane.smith@contoso.com" }' # Response: 201 Created # Location: https://api.contoso.com/v1.0/users/jane.smith@contoso.com # PUT - Create or replace entire resource (client-specified ID) curl -X PUT "https://api.contoso.com/v1.0/users/custom-id-123" \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/json" \ -d '{ "displayName": "Bob Wilson", "email": "bob.wilson@contoso.com" }' # Response: 201 Created (new) or 200 OK (replaced) # PATCH - Partial update using JSON Merge Patch (RFC 7396) curl -X PATCH "https://api.contoso.com/v1.0/users/user@contoso.com" \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/merge-patch+json" \ -d '{ "displayName": "John Updated" }' # Response: 200 OK # DELETE - Remove a resource curl -X DELETE "https://api.contoso.com/v1.0/users/user@contoso.com" \ -H "Authorization: Bearer {token}" # Response: 204 No Content ``` --- ## URL Structure and Resource Naming Consistent URL patterns for Azure and Microsoft Graph services. ```bash # Azure URL Pattern # https://.../// curl -X GET "https://contoso.eastus.blobstore.azure.net/containers/mycontainer/blobs/myfile.txt" \ -H "Authorization: Bearer {token}" # Microsoft Graph URL Pattern # https://graph.microsoft.com/{version}/{category}/{pathSegment} curl -X GET "https://graph.microsoft.com/v1.0/users/user@contoso.com/messages" \ -H "Authorization: Bearer {token}" # Action operations use colon syntax curl -X POST "https://api.contoso.com/v1.0/users/user@contoso.com:sendMail" \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/json" \ -d '{ "subject": "Hello", "body": "Message content" }' # Response: 200 OK # Collection action operations curl -X POST "https://api.contoso.com/v1.0/users:export" \ -H "Authorization: Bearer {token}" # Response: 200 OK ``` --- ## API Versioning Date-based versioning using required api-version query parameter. ```bash # Required api-version parameter on every request curl -X GET "https://api.contoso.com/users?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Preview versions include -preview suffix curl -X GET "https://api.contoso.com/users?api-version=2024-03-01-preview" \ -H "Authorization: Bearer {token}" # Missing api-version returns 400 Bad Request curl -X GET "https://api.contoso.com/users" \ -H "Authorization: Bearer {token}" # Response: 400 Bad Request # { # "error": { # "code": "MissingApiVersionParameter", # "message": "The api-version query parameter (?api-version=) is required for all requests" # } # } # Unsupported api-version returns error with valid versions curl -X GET "https://api.contoso.com/users?api-version=1999-01-01" \ -H "Authorization: Bearer {token}" # Response: 400 Bad Request # { # "error": { # "code": "UnsupportedApiVersionValue", # "message": "Unsupported api-version '1999-01-01'. The supported api-versions are '2024-01-15, 2023-06-01'." # } # } ``` --- ## Error Response Format Standard error schema with code, message, and nested details. ```bash # Error response structure curl -X POST "https://api.contoso.com/v1.0/users" \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/json" \ -d '{"invalid": "data"}' # Response: 400 Bad Request # x-ms-error-code: InvalidPasswordFormat # { # "error": { # "code": "InvalidPasswordFormat", # "message": "Human-readable description", # "target": "password", # "innererror": { # "code": "PasswordTooShort", # "minLength": 6 # } # } # } # Multiple validation errors in details array # Response: 400 Bad Request # { # "error": { # "code": "BadArgument", # "message": "Multiple errors in ContactInfo data", # "target": "ContactInfo", # "details": [ # { # "code": "NullValue", # "target": "PhoneNumber", # "message": "Phone number must not be null" # }, # { # "code": "MalformedValue", # "target": "Address", # "message": "Address is not valid" # } # ] # } # } # 403 vs 404 for security - return 404 to avoid information disclosure curl -X GET "https://api.contoso.com/v1.0/secrets/hidden-resource" \ -H "Authorization: Bearer {limited-token}" # Response: 404 Not Found (instead of 403 to not leak existence) ``` --- ## Collection Pagination Server-driven paging with nextLink for large collections. ```bash # Initial request to collection curl -X GET "https://api.contoso.com/v1.0/users?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Response: 200 OK # { # "value": [ # {"id": "user1", "displayName": "Alice", "etag": "\"abc\""}, # {"id": "user2", "displayName": "Bob", "etag": "\"def\""} # ], # "nextLink": "https://api.contoso.com/v1.0/users?api-version=2024-01-15&$skiptoken=opaque-token" # } # Follow nextLink for next page (treat as opaque URL) curl -X GET "https://api.contoso.com/v1.0/users?api-version=2024-01-15&\$skiptoken=opaque-token" \ -H "Authorization: Bearer {token}" # Response: 200 OK (last page has no nextLink) # { # "value": [ # {"id": "user3", "displayName": "Charlie", "etag": "\"ghi\""} # ] # } # Client-driven paging with $top and $skip curl -X GET "https://api.contoso.com/v1.0/users?\$top=10&\$skip=20&api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Request maximum page size preference curl -X GET "https://api.contoso.com/v1.0/users?maxpagesize=50&api-version=2024-01-15" \ -H "Authorization: Bearer {token}" ``` --- ## Filtering and Sorting OData-style query parameters for filtering and ordering collections. ```bash # Simple equality filter curl -X GET "https://api.contoso.com/v1.0/products?filter=name%20eq%20'Milk'&api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Compound filter with AND/OR curl -X GET "https://api.contoso.com/v1.0/products?filter=(name%20eq%20'Milk'%20or%20name%20eq%20'Eggs')%20and%20price%20lt%202.55&api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Range filter curl -X GET "https://api.contoso.com/v1.0/products?filter=price%20ge%2010%20and%20price%20le%20100&api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Sorting ascending (default) curl -X GET "https://api.contoso.com/v1.0/users?orderby=displayName&api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Sorting descending with secondary sort curl -X GET "https://api.contoso.com/v1.0/users?orderby=displayName%20desc,createdAt&api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Combined filter, sort, and pagination curl -X GET "https://api.contoso.com/v1.0/products?filter=price%20gt%2020&orderby=name&\$top=10&api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Select specific properties curl -X GET "https://api.contoso.com/v1.0/users?\$select=id,displayName,email&api-version=2024-01-15" \ -H "Authorization: Bearer {token}" ``` --- ## Conditional Requests and ETags Optimistic concurrency control using If-Match and If-None-Match headers. ```bash # GET returns ETag header curl -X GET "https://api.contoso.com/v1.0/users/user1?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Response Headers: ETag: "67ab43" # Response Body: {"id": "user1", "displayName": "Alice", "etag": "67ab43"} # Conditional GET - returns 304 if unchanged curl -X GET "https://api.contoso.com/v1.0/users/user1?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" \ -H "If-None-Match: \"67ab43\"" # Response: 304 Not Modified (no body, use cached version) # Update only if resource unchanged (optimistic concurrency) curl -X PATCH "https://api.contoso.com/v1.0/users/user1?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/merge-patch+json" \ -H "If-Match: \"67ab43\"" \ -d '{"displayName": "Alice Updated"}' # Response: 200 OK (if ETag matched) # Response: 412 Precondition Failed (if resource changed by another client) # Create only if resource doesn't exist curl -X PUT "https://api.contoso.com/v1.0/users/newuser?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/json" \ -H "If-None-Match: *" \ -d '{"displayName": "New User"}' # Response: 201 Created (if new) # Response: 412 Precondition Failed (if already exists) ``` --- ## Long-Running Operations (LRO) Asynchronous operations with status monitor polling. ```bash # Initiate LRO with PUT (creates resource with long-running processing) curl -X PUT "https://api.contoso.com/v1.0/databases/db1?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" \ -H "Operation-Id: operation-123" \ -H "Content-Type: application/json" \ -d '{"prop1": 555, "prop2": "something"}' # Response: 201 Created # Operation-Id: operation-123 # Operation-Location: https://api.contoso.com/v1.0/operations/operation-123 # { # "id": "db1", # "status": "Provisioning", # "prop1": 555, # "prop2": "something" # } # Poll status monitor curl -X GET "https://api.contoso.com/v1.0/operations/operation-123?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Response: 200 OK (still processing) # Retry-After: 30 # { # "id": "operation-123", # "status": "Running" # } # Poll again after Retry-After delay curl -X GET "https://api.contoso.com/v1.0/operations/operation-123?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Response: 200 OK (completed) # { # "id": "operation-123", # "status": "Succeeded" # } # Long-running action operation curl -X POST "https://api.contoso.com/v1.0/users/user1:sendBulkEmail?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" \ -H "Operation-Id: email-batch-456" \ -H "Content-Type: application/json" \ -d '{"recipients": ["a@b.com", "c@d.com"], "template": "welcome"}' # Response: 202 Accepted # Operation-Location: https://api.contoso.com/v1.0/operations/email-batch-456 # { # "id": "email-batch-456", # "status": "NotStarted" # } # Cancel a long-running operation curl -X POST "https://api.contoso.com/v1.0/operations/email-batch-456:cancel?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Response: 200 OK # { # "id": "email-batch-456", # "status": "Canceled" # } ``` --- ## Repeatability Headers for Idempotent POST Safe retry mechanism for POST operations using repeatability headers. ```bash # First POST request with repeatability headers curl -X POST "https://api.contoso.com/v1.0/orders?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/json" \ -H "Repeatability-Request-ID: 4227cdc5-9f48-4e84-921a-10967cb785a0" \ -H "Repeatability-First-Sent: Sun, 06 Nov 2024 08:49:37 GMT" \ -d '{"product": "Widget", "quantity": 5}' # Response: 201 Created # Repeatability-Result: accepted # Location: https://api.contoso.com/v1.0/orders/order-123 # { # "id": "order-123", # "product": "Widget", # "quantity": 5 # } # Retry with same headers (client didn't receive response) curl -X POST "https://api.contoso.com/v1.0/orders?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/json" \ -H "Repeatability-Request-ID: 4227cdc5-9f48-4e84-921a-10967cb785a0" \ -H "Repeatability-First-Sent: Sun, 06 Nov 2024 08:49:37 GMT" \ -d '{"product": "Widget", "quantity": 5}' # Response: 201 Created (same response as original - idempotent) # Repeatability-Result: accepted ``` --- ## Throttling and Rate Limits Handling 429 Too Many Requests and 503 Service Unavailable responses. ```bash # Request that triggers rate limiting curl -X GET "https://api.contoso.com/v1.0/users?api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Response: 429 Too Many Requests # Retry-After: 5 # RateLimit-Limit: 1000 # RateLimit-Remaining: 0 # RateLimit-Reset: 1538152773 # { # "error": { # "code": "requestLimitExceeded", # "message": "The caller has made too many requests in the time period." # } # } # Service overload response (not caller's fault) # Response: 503 Service Unavailable # Retry-After: 30 # { # "error": { # "code": "serviceUnavailable", # "message": "The service is temporarily unavailable. Please retry after the time specified in Retry-After header." # } # } # Client retry logic (pseudocode) # while response.status in [429, 503]: # wait(response.headers['Retry-After']) # response = retry_request() ``` --- ## Microsoft Graph Specific Patterns OData-based queries for Microsoft Graph API. ```bash # Expand navigation properties curl -X GET "https://graph.microsoft.com/v1.0/users/user@contoso.com?\$expand=manager&api-version=2024-01-15" \ -H "Authorization: Bearer {token}" # Response: 200 OK # { # "id": "user@contoso.com", # "displayName": "John Doe", # "manager": { # "id": "manager@contoso.com", # "displayName": "Jane Manager" # } # } # Delta queries for change tracking curl -X GET "https://graph.microsoft.com/v1.0/users/delta" \ -H "Authorization: Bearer {token}" # Response: 200 OK # { # "value": [ # {"id": "user1", "displayName": "Alice"}, # {"id": "user2", "@removed": {}} # ], # "@odata.deltaLink": "https://graph.microsoft.com/v1.0/users/delta?$deltatoken=opaque" # } # Batch requests to reduce round trips curl -X POST "https://graph.microsoft.com/v1.0/\$batch" \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/json" \ -d '{ "requests": [ {"id": "1", "method": "GET", "url": "/me"}, {"id": "2", "method": "GET", "url": "/me/messages?$top=5"} ] }' # Response: 200 OK # { # "responses": [ # {"id": "1", "status": 200, "body": {"displayName": "John"}}, # {"id": "2", "status": 200, "body": {"value": [...]}} # ] # } ``` --- ## Deprecation Notification Header Communicating upcoming breaking changes via response headers. ```bash # Response includes deprecation warning curl -X GET "https://api.contoso.com/v1.0/legacy-endpoint?api-version=2020-01-01" \ -H "Authorization: Bearer {token}" # Response: 200 OK # azure-deprecating: API version 2020-01-01 will retire on 2025-01-01 (https://azure.microsoft.com/updates/legacy-api-retirement) # { # "data": "response content" # } # Multiple deprecations in header # azure-deprecating: API version 2020-01-01 will retire on 2025-01-01 (https://azure.microsoft.com/updates/v1);TLS 1.0 & 1.1 will retire on 2025-06-01 (https://azure.microsoft.com/updates/tls) ``` --- ## Summary The Microsoft REST API Guidelines provide a comprehensive framework for building consistent, developer-friendly APIs across Microsoft's cloud services. The primary use cases include building Azure data plane services that need to expose resources via HTTP/REST interfaces, creating Microsoft Graph workloads that integrate with the broader Microsoft 365 ecosystem, and designing any RESTful API that requires enterprise-grade patterns for versioning, error handling, pagination, and long-running operations. The guidelines emphasize idempotency across all operations, proper HTTP status codes, comprehensive error responses with actionable messages, and backward compatibility guarantees that protect customer investments. Integration patterns center around standard HTTP conventions with OData enhancements for Microsoft Graph. Services should implement API versioning from the first release using date-based version strings, support conditional requests via ETags for caching and optimistic concurrency, and use the repeatability headers pattern for safely retrying POST operations. Long-running operations follow a consistent status monitor pattern that enables clients to track asynchronous work without blocking. The guidelines encourage an API-first design approach where teams define their OpenAPI contracts before implementation, engage with API stewardship boards for review, and iterate through multiple preview releases to gather customer feedback before GA release. All APIs should support standard query options (filtering, sorting, pagination, property selection) to enable flexible client consumption across multiple programming languages and platforms.