Loading learning content...
When Netflix engineers build APIs for their mobile apps to fetch movie recommendations, they face fundamentally different constraints than when they build APIs for internal services to communicate about user preferences. Both are APIs. Both follow similar technical patterns. Yet the design decisions, security models, versioning strategies, and operational requirements diverge dramatically.
The distinction between internal APIs (consumed within your organization) and external APIs (consumed by third parties) is one of the most consequential architectural decisions in API design. Getting this classification wrong leads to either over-engineered internal APIs that slow development velocity, or under-protected external APIs that create security vulnerabilities and break partner integrations.
This page provides a rigorous framework for understanding these two API categories, their unique requirements, and how to design appropriately for each.
You will understand the fundamental differences between internal and external APIs across dimensions including: trust models, security requirements, versioning constraints, documentation standards, SLA expectations, and governance approaches. You'll learn when to apply which set of constraints and how to design APIs that serve their intended audience effectively.
Before diving into differences, we need precise definitions. The internal/external distinction isn't always as simple as 'inside the company' versus 'outside.'
Internal APIs are consumed exclusively by systems and teams within the same organization. They enable:
Key characteristics:
External APIs are consumed by parties outside your organization. They subdivide into:
Public APIs — Available to any developer
Partner APIs — Available to specific business partners
Key characteristics:
| Characteristic | Internal APIs | External APIs |
|---|---|---|
| Consumer identity | Known employees/systems | Unknown developers/companies |
| Communication channel | Direct (Slack, meetings) | Indirect (docs, support tickets) |
| Trust level | High (same org) | Low (untrusted by default) |
| Breaking change cost | Coordination within org | Unknown cascade effects |
| Implementation visibility | Source code accessible | Black box to consumers |
| Usage control | Can enforce policies | Can only suggest patterns |
Quick heuristic: If you need a developer portal for consumer self-service, you're building an external API. If you can just ping the consuming team on Slack when something changes, you might be building an internal API. This isn't definitive, but it helps identify which category your API falls into.
Security is the dimension where internal and external APIs diverge most dramatically. The fundamental difference lies in the trust model.
Internal APIs operate within a trusted network perimeter (or zero-trust mesh). Key security patterns:
Service-to-Service Authentication:
Authorization:
Network Security:
External APIs must assume hostile consumers. Every request could be an attack:
123456789101112131415161718192021222324252627282930313233343536373839404142
// Internal API: Trust within network boundary// Lighter security, relies on network isolation import { FastifyRequest, FastifyReply } from 'fastify'; // Internal service-to-service authenticationasync function internalAuthMiddleware( request: FastifyRequest, reply: FastifyReply) { // Verify mTLS client certificate (handled by service mesh) const serviceIdentity = request.headers['x-forwarded-client-cert']; if (!serviceIdentity) { // If no mTLS, fall back to internal JWT const internalToken = request.headers['x-internal-service-token']; if (!verifyInternalToken(internalToken)) { return reply.status(401).send({ error: 'Invalid service identity' }); } } // Authorization is simpler - service X can call service Y // Often implicit based on network topology const callingService = extractServiceName(serviceIdentity); request.callingService = callingService; // Log for audit but don't add heavy overhead request.log.debug({ caller: callingService, endpoint: request.url });} // Less aggressive rate limiting for internal servicesconst internalRateLimits = { max: 10000, // High limits for trusted services timeWindow: '1m', keyGenerator: (req) => req.callingService, // Per-service limits};| Security Measure | Internal APIs | External APIs |
|---|---|---|
| Authentication | mTLS, service mesh, internal JWT | API keys, OAuth 2.0, JWT |
| Authorization | Service-level, coarse-grained | User-level, fine-grained scopes |
| Rate limiting | High limits, per-service | Low limits, per-key/user |
| Input validation | Trust service contracts | Validate everything, trust nothing |
| Encryption | Service mesh (often transparent) | TLS required, pinning recommended |
| Audit logging | Operational focus | Compliance + security focus |
| Abuse detection | Anomaly monitoring | Active threat detection + blocking |
| Error messages | Can include details | Sanitized, no internal info |
A common security vulnerability is accidentally exposing internal APIs to external networks. Internal APIs lack the defensive depth required for hostile environments. Use network segmentation, API gateways, and regular security audits to ensure internal APIs remain internal.
API versioning requirements differ dramatically based on consumer accessibility. This is where the true cost of external APIs becomes apparent.
Internal APIs can use aggressive versioning because:
Common internal versioning approaches:
No explicit versioning — Just change the API and update all consumers in the same deployment. Works in monorepos with shared deployment.
Date-based versioning — api-2024-01-15 for internal documentation. Change freely, document what changed.
Feature flags — Toggle new behavior for internal testing before making it default.
External APIs require conservative versioning because:
Essential external versioning principles:
api.example.com/v1/ or API-Version: 2024-01-15123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
# OpenAPI specification with URL versioningopenapi: 3.0.0info: title: External Payment API version: 2.0.0 # Semantic version of this spec description: | ## API Versioning This API uses URL path versioning. **Current version:** /v2 **Supported versions:** /v1 (deprecated), /v2 ### Version 1 Deprecation - **Deprecated:** January 1, 2024 - **Sunset date:** July 1, 2024 - **Migration guide:** https://docs.example.com/migrate-v1-v2 ### Breaking Changes in v2 - `amount` field changed from cents (integer) to Money object - `customer_id` renamed to `customerId` for consistency - Removed `charge` endpoint (use `payments` instead) servers: - url: https://api.example.com/v2 description: Production (current) - url: https://api.example.com/v1 description: Production (deprecated - sunset July 2024) paths: /payments: post: operationId: createPayment summary: Create a payment requestBody: required: true content: application/json: schema: type: object required: - amount - customerId properties: amount: $ref: '#/components/schemas/Money' # v2: structured object customerId: type: string description: Customer identifier (renamed from customer_id in v1)Hyrum's Law states: 'With a sufficient number of users, every observable behavior of your API will be depended upon by someone.' For external APIs, this means even 'internal implementation details' that leak through responses become part of your de facto contract. Be extremely careful about what you expose.
Documentation requirements scale dramatically with API externality. The key insight is that internal teams have tacit knowledge that external developers lack.
Internal documentation can be leaner because:
Minimum internal doc requirements:
External documentation must be comprehensive and self-service:
External documentation requirements:
| Documentation Element | Internal API | External API |
|---|---|---|
| Time to maintain ratio | 10% of dev time | 30-50% of dev time |
| Languages covered | Company primary language | 5-10 major languages |
| Update frequency | With releases | Continuous, real-time |
| Review process | Peer review | Technical writer + editorial |
| User feedback integration | Slack channel | Formal feedback loops |
| Versioning | Latest only | All supported versions |
For external APIs, documentation IS part of the product. Poor documentation causes integration failures, increases support costs, and drives developers to competitors. Budget documentation effort into external API development from day one.
Operational expectations—SLAs, monitoring, and incident response—differ significantly between internal and external APIs.
Internal APIs operate under shared fate with their consumers:
Typical internal API operational model:
123456789101112131415161718192021222324252627282930313233
# Internal API Service Level Objectives (SLOs)# These are targets, not contractual guarantees service: user-serviceslo: availability: target: 99.9% measurement_window: 30d # Note: No financial penalties, just engineering priority escalation: - threshold: 99.5% action: Slack alert to team - threshold: 99% action: Incident declared, cross-team sync latency: p50: 50ms p95: 200ms p99: 500ms # Measured at service mesh, not external error_rate: target: < 0.1% # Excludes 4xx client errors monitoring: dashboards: - url: https://grafana.internal/user-service alerting: on_call: user-service-team slack: "#user-service-alerts" # No external status page neededExternal APIs operate under contractual obligations with real consequences:
When external APIs fail, customers discover it through their own monitoring. Twitter fills with complaints. Support tickets spike. Unlike internal failures, you cannot control the narrative. This is why external APIs require redundancy, geographic distribution, and aggressive monitoring that internal APIs may not justify.
API governance—the policies and processes for API design, review, and evolution—must scale with API externality.
Internal APIs benefit from lightweight governance:
Appropriate internal governance:
External APIs require formal governance because:
External API governance requirements:
| Governance Element | Internal API | External API |
|---|---|---|
| Design approval | Team lead | API Review Board |
| Breaking changes | Coordinate with consumers | 12+ month deprecation cycle |
| Naming conventions | Suggested style guide | Enforced standards |
| Security review | Automated scanning | Manual security audit |
| Documentation | Team responsibility | Technical writer required |
| Launch process | Feature flag rollout | Formal GA announcement |
Heavy governance for external APIs isn't bureaucracy—it's investment protection. A poorly designed external API creates years of support burden, damages developer trust, and becomes permanent legacy. The upfront governance cost is 10x cheaper than fixing a public API mistake.
Between fully internal and fully public APIs exists a spectrum of partner APIs with hybrid characteristics.
Partner APIs serve known external parties under negotiated terms:
Good candidates for partner APIs:
When to avoid partner APIs (go public instead):
| Dimension | Internal Approach | External Approach | Partner API Approach |
|---|---|---|---|
| Onboarding | Automatic access | Self-service portal | Sales-assisted, custom setup |
| Rate limits | High defaults | Fixed tiers | Negotiated per-partner |
| SLA | Implicit | Published tiers | Custom per agreement |
| Support | Slack/internal | Ticket-based | Dedicated account manager |
| Breaking changes | Coordinate | Long deprecation | Direct partner communication |
| Documentation | Internal docs | Public docs | Public + partner-specific guides |
| Versioning | Aggressive | Conservative | Partner-coordinated |
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
// Partner API configuration management// Each partner can have custom limits, features, and access interface PartnerConfig { id: string; name: string; // Custom rate limits negotiated per contract rateLimits: { requestsPerMinute: number; requestsPerDay: number; burstAllowance: number; }; // Feature flags for gradual rollout enabledFeatures: Set<string>; // API versions this partner has access to allowedVersions: string[]; // Custom SLA tier slaTier: 'standard' | 'premium' | 'enterprise'; // Support configuration support: { dedicatedContactEmail: string; slackChannel?: string; escalationPath: string[]; }; // Webhook configuration webhooks: { endpoints: string[]; signingSecret: string; retryPolicy: 'standard' | 'aggressive'; };} // Example partner configurationsconst partnerConfigs: PartnerConfig[] = [ { id: 'partner_acme_corp', name: 'ACME Corporation', rateLimits: { requestsPerMinute: 10000, // 100x standard requestsPerDay: 5000000, burstAllowance: 1.5, }, enabledFeatures: new Set([ 'beta-analytics-api', // Early access 'bulk-operations', // Enterprise feature 'custom-webhooks', ]), allowedVersions: ['2023-06-01', '2024-01-15'], slaTier: 'enterprise', support: { dedicatedContactEmail: 'acme-support@example.com', slackChannel: '#acme-integration', escalationPath: ['account-manager@example.com', 'cto@example.com'], }, webhooks: { endpoints: ['https://acme.com/webhooks/example'], signingSecret: 'whsec_...', retryPolicy: 'aggressive', }, },];Many successful public APIs started as partner APIs. This path allows you to learn from a small group of sophisticated integrators before opening to the masses. Stripe, Twilio, and AWS all used early partner programs to refine their APIs before general availability.
The internal/external distinction fundamentally shapes every API design decision. Let's consolidate the key frameworks:
What's next:
With the internal/external framework established, we'll explore APIs as products—how product thinking transforms API design from a technical exercise into a strategic business activity.
You now have a framework for classifying APIs and understanding how that classification changes design decisions. This framework will inform every subsequent topic—from REST design to versioning to gateway patterns. Keep asking 'internal or external?' as your first design question.