Getting Started

The process is simple:
  1. Fill up a Google Form Request an API Key
  2. Wait for admin to review. It might take 1-2 days to receive API Key.
  3. Once you’re approved, you will get an API Key.
Free tier includes 3,000 requests per month (100 requests per day) - perfect for development and small applications.
Yes! Our Community (Free) plan includes:
  • 3,000 requests per month
  • 100 requests per day
  • 2 requests per second rate limit
  • Access to all endpoints
  • No credit card required
This is perfect for development, testing, and small-scale applications.
You can make your first API call within minutes:
curl -X GET 'https://api.countrystatecity.in/v1/countries' \
  -H 'X-CSCAPI-KEY: YOUR_API_KEY'
Your API key is ready to use immediately after registration - no waiting period required.
While we don’t maintain official SDKs, our RESTful API works with any programming language that can make HTTP requests. We provide comprehensive integration examples for:
  • JavaScript/Node.js
  • Python
  • PHP
  • Java
  • C#
  • Go
  • Ruby
Community members have also created unofficial libraries for various platforms.

Authentication & Security

Authentication is done using API key headers. Include your API key in every request:
X-CSCAPI-KEY: YOUR_API_KEY
const response = await fetch('https://api.countrystatecity.in/v1/countries', {
  headers: { 'X-CSCAPI-KEY': 'YOUR_API_KEY' }
});
Never expose your API key in client-side code such as browser JavaScript, mobile apps, or public repositories.
Safe practices:
  • Use server-side applications or serverless functions
  • Store API keys in environment variables
  • Implement your own backend API that securely calls our API
  • Use build-time environment variables for static sites
Why this matters:
  • API keys in client-side code are visible to anyone
  • Malicious users could extract and abuse your API key
  • This could lead to unexpected usage and charges
Yes! You can manage your API keys through the developer portal:
  1. Log into your account
  2. Navigate to API key management
  3. Generate a new key
  4. Update your applications with the new key
  5. Revoke the old key once migration is complete
Keep both keys active during migration to avoid service interruption.
A 401 error typically indicates an authentication issue:Common causes and solutions:
  • Invalid API key: Double-check your API key for typos
  • Wrong header name: Ensure you’re using X-CSCAPI-KEY exactly
  • Missing header: Verify the header is being sent with every request
  • Expired/revoked key: Generate a new API key if needed
Test your authentication:
curl -X GET 'https://api.countrystatecity.in/v1/countries' \
  -H 'X-CSCAPI-KEY: YOUR_API_KEY' \
  -w "HTTP Status: %{http_code}\n"
A successful test returns HTTP 200 with country data.

Rate Limits & Usage

Rate limits vary by plan:
PlanMonthly RequestsDaily RequestsRate Limit
Community (Free)3,0001002 req/second
Supporter30,0001,00010 req/second
Professional150,0005,00025 req/second

View Full Pricing Details

Compare plans, features, and pricing options
Every API response includes rate limit headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200
X-RateLimit-Window: 3600
Monitor usage in code:
const response = await fetch(apiUrl, { headers });

const remaining = response.headers.get('X-RateLimit-Remaining');
const limit = response.headers.get('X-RateLimit-Limit');

console.log(`Rate Limit: ${remaining}/${limit}`);

if (remaining < limit * 0.1) {
  console.warn('⚠️ Approaching rate limit!');
}
You can also monitor usage through your account dashboard.
When you exceed your rate limit, you’ll receive a 429 Too Many Requests response:
{
  "error": "Rate limit exceeded",
  "message": "Too many requests. Please try again later.",
  "retry_after": 3600
}
Best practices for handling rate limits:
  • Implement exponential backoff retry logic
  • Cache responses to reduce API calls
  • Monitor usage proactively
  • Consider upgrading your plan if limits are consistently hit
The Retry-After header indicates how long to wait before making another request.
Effective caching strategies:
  • Cache country/state data for 24+ hours (rarely changes)
  • Cache city data for 12+ hours
  • Implement local fallbacks for critical data
Efficient request patterns:
  • Fetch broader datasets when possible (all countries vs individual requests)
  • Use hierarchical endpoints strategically
  • Batch related requests together
Example caching implementation:
class APICache {
  constructor(ttl = 24 * 60 * 60 * 1000) {
    this.cache = new Map();
    this.ttl = ttl;
  }
  
  get(key) {
    const item = this.cache.get(key);
    if (!item || Date.now() - item.timestamp > this.ttl) {
      return null;
    }
    return item.data;
  }
  
  set(key, data) {
    this.cache.set(key, { data, timestamp: Date.now() });
  }
}

Data & Endpoints

Countries include:
  • ISO2 and ISO3 codes
  • Country name and native name
  • Phone code and currency information
  • Flag emoji and region information
  • Latitude and longitude coordinates
States include:
  • State name and ISO2 code
  • Country association
  • State type (state, province, region, etc.)
  • Latitude and longitude coordinates
Cities include:
  • City name and unique ID
  • Country and state association
  • Latitude and longitude coordinates
  • Timezone information (when available)
Use the detailed endpoints (/countries/{iso2}) to get complete information including additional metadata.
Our data is continuously maintained and updated:
  • Country data: Updated as needed for political changes, currency updates, etc.
  • State/Province data: Updated for administrative boundary changes
  • City data: Regular additions and corrections based on authoritative sources
We implement automated validation and quality checks to ensure data accuracy. Major updates are announced through our changelog.
The dataset includes 247+ countries, 5,000+ states/provinces, and 151,000+ cities worldwide.
No, for performance and efficiency reasons, our API is designed with hierarchical endpoints:
  • /countries - All countries
  • /countries/{countryCode}/states - States within a country
  • /countries/{countryCode}/cities - Cities within a country
  • /countries/{countryCode}/states/{stateCode}/cities - Cities within a state
Why this design:
  • Faster response times for specific data
  • Reduced bandwidth usage
  • Better caching opportunities
  • More manageable response sizes
Requesting all cities globally would result in 151,000+ records, which would be slow and consume significant bandwidth.
Yes! Latitude and longitude coordinates are included for:
  • Countries: Approximate center coordinates
  • States/Provinces: Administrative center coordinates
  • Cities: City center coordinates
Example city response:
{
  "id": 12345,
  "name": "Los Angeles",
  "latitude": "34.05223",
  "longitude": "-118.24368",
  "country_code": "US",
  "state_code": "CA"
}
Coordinates are provided as strings for precision and can be easily converted to numbers for calculations.
Yes, we follow international standards:Country Codes:
  • ISO 3166-1 alpha-2 (2-letter codes like “US”, “GB”)
  • ISO 3166-1 alpha-3 (3-letter codes like “USA”, “GBR”)
Currency Codes:
  • ISO 4217 standard (3-letter codes like “USD”, “EUR”)
Phone Codes:
  • ITU-T E.164 standard international calling codes
This ensures compatibility with other international systems and databases.

Integration & Development

We strongly recommend against making direct API calls from browser applications due to security concerns with exposing API keys.
Recommended approaches:
  1. Server-side proxy: Create your own API endpoint that calls our API
  2. Serverless functions: Use services like Vercel Functions, Netlify Functions, or AWS Lambda
  3. Static site generation: Fetch data at build time for static sites
Why avoid direct browser calls:
  • API keys are visible in browser developer tools
  • Risk of key theft and unauthorized usage
  • Potential CORS restrictions
Implement cascading dropdowns by chaining API calls based on user selections:React example pattern:
const [selectedCountry, setSelectedCountry] = useState('');
const [states, setStates] = useState([]);
const [cities, setCities] = useState([]);

// Load states when country changes
useEffect(() => {
  if (selectedCountry) {
    loadStates(selectedCountry);
    setCities([]); // Clear cities when country changes
  }
}, [selectedCountry]);

const loadStates = async (countryCode) => {
  const statesData = await api.getStates(countryCode);
  setStates(statesData);
};

Complete Example

See our comprehensive cascading dropdown implementation guide
Implement comprehensive error handling for different scenarios:Error types and handling:
async function makeAPIRequest(endpoint) {
  try {
    const response = await fetch(endpoint, { headers });
    
    if (response.status === 401) {
      throw new Error('INVALID_API_KEY');
    } else if (response.status === 429) {
      throw new Error('RATE_LIMITED');
    } else if (response.status === 404) {
      throw new Error('NOT_FOUND');
    } else if (!response.ok) {
      throw new Error('API_ERROR');
    }
    
    return await response.json();
    
  } catch (error) {
    if (error.message === 'RATE_LIMITED') {
      // Implement exponential backoff
      await delay(5000);
      return makeAPIRequest(endpoint);
    } else if (error.message === 'INVALID_API_KEY') {
      // Notify user to check API configuration
      console.error('Invalid API key configuration');
    }
    throw error;
  }
}
Best practices:
  • Always check response status codes
  • Implement retry logic for temporary failures
  • Provide meaningful error messages to users
  • Log errors for debugging purposes
Yes, caching is highly recommended and beneficial:Recommended cache durations:
  • Countries: 24-48 hours (very stable data)
  • States: 12-24 hours (occasionally updated)
  • Cities: 6-12 hours (more frequently updated)
Implementation strategies:
// Browser localStorage
const cacheKey = 'countries_data';
const cachedData = localStorage.getItem(cacheKey);
const isExpired = Date.now() - cachedData?.timestamp > 24 * 60 * 60 * 1000;

if (cachedData && !isExpired) {
  return JSON.parse(cachedData).data;
}

// Server-side caching (Redis, Memcached, etc.)
const redis = require('redis');
const client = redis.createClient();

const cached = await client.get('countries');
if (cached) return JSON.parse(cached);

const data = await fetchFromAPI();
await client.setex('countries', 86400, JSON.stringify(data)); // 24h TTL
Caching reduces API usage, improves performance, and provides offline fallbacks.

Troubleshooting

This can happen for several reasons:Common causes:
  1. Incorrect country/state codes: Verify you’re using the correct ISO codes
  2. Case sensitivity: Country codes should be uppercase (“US”, not “us”)
  3. State code format: Use 2-letter state codes when available
  4. Data availability: Some territories may have limited subdivision data
Debugging steps:
// First, verify the country exists
const countries = await api.getCountries();
const country = countries.find(c => c.iso2 === 'US');
console.log('Country found:', country);

// Then check states
const states = await api.getStates('US');
console.log('States count:', states.length);
Use our detailed country endpoint to verify exact country and state codes available.
Performance optimization strategies:
  1. Implement caching (most important)
  2. Use specific endpoints instead of filtering large datasets
  3. Parallel requests for independent data
  4. CDN/Edge caching for static geographical data
Example parallel requests:
// Instead of sequential requests
const countries = await api.getCountries();
const states = await api.getStates('US');

// Use parallel requests
const [countries, states] = await Promise.all([
  api.getCountries(),
  api.getStates('US')
]);
Server-side optimizations:
  • Use HTTP/2 for multiplexed requests
  • Implement connection pooling
  • Add response compression
This is typically due to caching issues or incorrect API usage:Check these common issues:
  1. Browser caching: Different cache states in development
  2. API versioning: Ensure you’re using the same API version
  3. Mixed endpoints: Don’t mix different API base URLs
  4. Concurrent updates: Our data is occasionally updated
Troubleshooting steps:
// Add request debugging
console.log('Request URL:', fullUrl);
console.log('Request headers:', headers);
console.log('Response status:', response.status);
console.log('Response headers:', [...response.headers.entries()]);
Force fresh data:
// Add cache-busting parameter for testing
const url = `${baseUrl}?_t=${Date.now()}`;
If issues persist, contact support with specific request/response examples.
Testing strategy:
  1. Start with cURL to verify basic connectivity
  2. Test each endpoint individually
  3. Verify error handling with invalid inputs
  4. Load test within your rate limits
Basic connectivity test:
curl -X GET 'https://api.countrystatecity.in/v1/countries' \
  -H 'X-CSCAPI-KEY: YOUR_API_KEY' \
  -w "HTTP Status: %{http_code}\n"
Automated testing example:
describe('Country State City API', () => {
  test('should fetch countries successfully', async () => {
    const countries = await api.getCountries();
    expect(countries).toBeInstanceOf(Array);
    expect(countries.length).toBeGreaterThan(0);
    expect(countries[0]).toHaveProperty('iso2');
  });

  test('should handle invalid country code', async () => {
    await expect(api.getStates('INVALID')).rejects.toThrow();
  });
});
A successful countries request should return 247+ countries with proper ISO codes.

Billing & Account

Billing details:
  • Monthly billing cycle
  • Usage resets on your billing date
  • Overage protection available
  • Multiple payment methods accepted
Plan upgrades:
  • Instant activation
  • Prorated billing for upgrades
  • Change or cancel anytime
Usage tracking:
  • Real-time usage monitoring in your dashboard
  • Email alerts for approaching limits
  • Detailed usage analytics and reporting

View Pricing & Plans

Compare features and choose the right plan for your needs
Yes! You can change plans anytime:Upgrades:
  • Immediate activation
  • Higher rate limits available instantly
  • Prorated billing for remainder of cycle
Downgrades:
  • Applied at next billing cycle
  • Usage limits adjust accordingly
  • Data access remains unchanged
How to change plans:
  1. Log into your account dashboard
  2. Navigate to billing/subscription settings
  3. Select your new plan
  4. Confirm changes
Contact support if you need help choosing the right plan for your usage patterns.
For monthly limits:
  • API requests are blocked until next billing cycle
  • Account dashboard shows exceeded status
  • Email notification sent when approaching and exceeding limits
For rate limits:
  • Requests return 429 status code
  • Retry after specified time period
  • No additional charges for rate limit hits
Overage protection:
  • Available on paid plans
  • Automatic temporary limit increases
  • Additional usage billed at standard rates
Free tier users cannot exceed monthly limits. Consider upgrading if you consistently hit limits.

Still Need Help?

Can’t find what you’re looking for? Contact our support team with specific questions about your integration or use case.