API Documentation
Free, public REST API for airfoil data and aerodynamic polars.
Overview
The Foil.tools API provides programmatic access to our database of >1,600 airfoils and their pre-computed aerodynamic polars across a wide range of Reynolds numbers.
Base URL
https://foil.tools/api/v1
Authentication
None required
Rate Limit
60 requests/hour
Rate Limiting
All API responses include rate limit headers to help you track your usage:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1710547200 (Unix timestamp)
When the limit is exceeded, the API returns a 429 status with a JSON body containing the reset time.
Endpoints
/api/v1API discovery endpoint. Returns available endpoints, descriptions, and rate limit info.
Example
Request
curl https://foil.tools/api/v1
Response
{
"version": "1",
"base_url": "https://foil.tools/api/v1",
"endpoints": [ ... ],
"rate_limit": { "requests_per_hour": 60, "auth": "none" }
}/api/v1/airfoilsList airfoils. Without filters returns all 1,600+ profiles. Supports fuzzy name search and geometric filters. Add geometry=1 to include thickness and camber fields.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| q | query | No | Fuzzy name search, e.g. "naca 23" or "eppler". |
| family | query | No | Filter by series family, e.g. "NACA 4-digit", "Eppler", "Selig". |
| t_min | query | No | Minimum thickness as a fraction (e.g. 0.10 = 10%). |
| t_max | query | No | Maximum thickness as a fraction. |
| camber_min | query | No | Minimum camber as a fraction. |
| camber_max | query | No | Maximum camber as a fraction. |
| geometry | query | No | Set to 1 to include thickness and camber values in the response. |
Example
Request
curl "https://foil.tools/api/v1/airfoils?family=Eppler&t_min=0.10&t_max=0.14&geometry=1"
Response
{
"count": 12,
"airfoils": [
{
"name": "Eppler 387",
"filename": "e387",
"family": "Eppler",
"source": "UIUC Database",
"thickness": 0.0907,
"camber": 0.0365
},
...
]
}/api/v1/airfoils/{name}Airfoil geometry as Selig-ordered coordinate pairs plus a summary (thickness, camber, TE thickness, LE weight). Add ?format=dat or Accept: text/plain for a raw XFOIL-compatible .dat file.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| name | path | Yes | Airfoil key, e.g. naca2412, e387, s1223. Case-insensitive. |
| format | query | No | Set to "dat" to return raw Selig .dat text instead of JSON. |
Example
Request
curl https://foil.tools/api/v1/airfoils/e387
Response
{
"name": "e387",
"display_name": "Eppler 387",
"family": "Eppler",
"summary": {
"max_thickness": 0.0907,
"max_camber": 0.0365,
"te_thickness": 0.0,
"le_weight": 0.172
},
"coordinates": [
{ "x": 1.0, "y": 0.00126 },
{ "x": 0.9982, "y": 0.00268 },
...
{ "x": 1.0, "y": -0.00126 }
]
}/api/v1/polars/{name}Aerodynamic polar rows (CL, CD, CM vs alpha) for a specific airfoil. Supports range filters on Re, alpha, CL, and CD.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| name | path | Yes | Airfoil key, e.g. naca2412, e387, s1223. Case-insensitive. |
| re_min | query | No | Minimum Reynolds number (e.g. 200000). |
| re_max | query | No | Maximum Reynolds number. |
| alpha_min | query | No | Minimum angle of attack in degrees. |
| alpha_max | query | No | Maximum angle of attack in degrees. |
| cl_min | query | No | Minimum lift coefficient. |
| cl_max | query | No | Maximum lift coefficient. |
Example
Request
curl "https://foil.tools/api/v1/polars/naca2412?re_min=500000&re_max=500000&alpha_min=0&alpha_max=8"
Response
{
"airfoil": "naca2412",
"source": "foil.tools",
"units": { "Re": "dimensionless", "alpha": "degrees", "CL": "dimensionless", "CD": "dimensionless", "CM": "dimensionless" },
"polars": [
{ "Re": 500000, "Ncrit": 9, "alpha": 0.0, "CL": 0.234, "CD": 0.00634, "CM": -0.0521 },
{ "Re": 500000, "Ncrit": 9, "alpha": 0.5, "CL": 0.289, "CD": 0.00628, "CM": -0.0522 },
...
]
}/api/v1/polars/searchRank airfoils by drag at a target operating point. Finds the closest available Reynolds bucket per airfoil, interpolates to the exact target CL in the pre-stall region, and returns matches sorted by ascending CD.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| cl | query | Yes | Target lift coefficient, e.g. 0.6. |
| re | query | Yes | Target Reynolds number, e.g. 500000. |
| re_tol | query | No | Fractional Re tolerance for bucket matching (default 0.10 = ±10%). |
| max_cd | query | No | Discard results with CD above this threshold. |
| alpha_max | query | No | Discard results where the interpolated alpha exceeds this value (degrees). |
| family | query | No | Filter to a specific airfoil family, e.g. "Eppler". |
| limit | query | No | Maximum number of results to return (default 20, max 100). |
Example
Request
curl "https://foil.tools/api/v1/polars/search?cl=0.6&re=500000&max_cd=0.012&limit=5"
Response
{
"query": { "cl": 0.6, "re": 500000, "re_tol": 0.1, "max_cd": 0.012 },
"count": 5,
"results": [
{
"airfoil": "e387",
"re_used": 500000,
"alpha": 3.21,
"CL": 0.6,
"CD": 0.00681,
"CM": -0.0831
},
...
]
}Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 404 | Airfoil not found |
| 429 | Rate limit exceeded (60 requests/hour) |
| 500 | Internal server error |
CORS
All /api/v1/* endpoints support cross-origin requests. The API returns Access-Control-Allow-Origin: * so you can call it directly from browser-based applications.