By the end of this tutorial, you will have a short Python script that turns the supercharge.info database into structured rows: one per Supercharger site, with its status, stall count, maximum power in kW, stall types, plug mix, opening date, coordinates and links to PlugShare, OpenStreetMap and Google Maps.
supercharge.info is a community-run tracker of Tesla Supercharger sites. It covers open sites and the build pipeline too: sites under construction, permitted or planned.
In this guide, we use Python and the Apify Actor Supercharger Database Scraper (reventadata/supercharge-scraper). It is unofficial and not affiliated with Tesla or supercharge.info. You will set filters, run the Actor and read the rows. The sample row below is a sample from a capture dated 2026-10-12, parsed with the Actor's own code.
⏱️ Time estimate: one sitting: install one package, set one token, run three short Python blocks.
Prerequisites
To follow this tutorial, you will need:
-
Python 3.11+ (
apify-client3.3.0 requires it). - The Apify API client for Python.
- An Apify account and its API token, which sits on the API & Integrations page in Apify Console.
Install the client:
pip install "apify-client~=3.0"
The code reads the token from an environment variable:
export APIFY_API_TOKEN="your_apify_token_here"
What you'll achieve
You will turn the database into one row per site. Each row carries the site id and url, name, status (OPEN, CONSTRUCTION, PLAN and so on), address fields, latitude and longitude, stallCount, powerKilowatt, a stalls breakdown by type, a plugs breakdown by standard, otherEVs (open to non-Tesla cars), dateOpened, and ready-made links to PlugShare, OpenStreetMap, Google Maps and Tesla's Find Us page.
Note: The rows carry no prices and no live stall availability. Per the Actor's README, supercharge.info does not publish prices, and stall counts are not "free right now" counts.
Example use case: Suppose you want every open site in Spain with at least 8 stalls. You would set country: "Spain", statuses: ["OPEN"] and minStalls: 8, and every saved row would be an open Spanish site of that size, or one whose stall count the source does not know.
Step 1: Access Supercharger Database Scraper
Go to the Supercharger Database Scraper page on Apify Store. The README lists every input and output field. In this tutorial we drive the Actor from Python, so everything after this step happens in code.
Step 2: Configure the filters
The Actor downloads the database's full site list in one request, then filters it. Every input is a filter on that list:
-
country: the country as supercharge.info names it (Spain,France,USA,United Kingdom). Matched in full, ignoring case. Empty means all. -
region:Europe,North America,Asia PacificorSouth America. Empty means all. -
statuses: any ofOPEN,EXPANDING,CONSTRUCTION,PERMIT,PLAN,VOTING,CLOSED_TEMP,CLOSED_PERM. Not set meansOPENonly; an empty list means every status. -
maxItems: the most sites to save (default 100).
Here is the setup. It asks for open sites in Spain with at least 8 stalls and saves at most 50:
import os
from apify_client import ApifyClient
# 1. Initialize ApifyClient with the token from the environment
client = ApifyClient(os.environ["APIFY_API_TOKEN"])
# 2. Define the filters: open sites in Spain with at least 8 stalls
run_input = {
"country": "Spain",
"statuses": ["OPEN"],
"minStalls": 8,
"maxItems": 50,
}
💡 Tip: spell country the way the source does. USA matches; United States does not, because the match is on the whole name.
No proxy setting is needed: the proxy is off by default, and the source served its list to direct requests when measured on 2026-09-22.
Step 3: Add optional filters
| Parameter | Type | What it keeps |
|---|---|---|
minStalls |
integer | Sites with at least N stalls (0 = off). Sites with an unknown count are kept. |
minPowerKw |
integer | Sites with maximum power of at least N kW (0 = off). Unknown power is kept. |
openToNonTeslaOnly |
boolean | Sites where non-Tesla EVs can charge. |
searchText |
string | Sites whose name, street, city, state, ZIP or country contains the text, ignoring case. |
proxyConfiguration |
object | Optional Apify Proxy, off by default. |
Three combinations that work:
-
The European build pipeline:
region: "Europe",statuses: ["CONSTRUCTION", "PERMIT", "PLAN"]. -
Fast sites any EV can use:
minPowerKw: 250,openToNonTeslaOnly: true. -
One metro area:
searchText: "madrid"withstatuses: []. It matches the state field too.
⚠️ A misspelt status, such as
OPENED, stops the run with an error that lists the known statuses.
Step 4: Run the Actor
Now start the run with client.actor("reventadata/supercharge-scraper").call(run_input=run_input). The call starts the Actor on Apify and waits for it to finish before returning the run:
# 3. Start the Actor run and wait for it to finish
print("Starting supercharge.info extraction...")
run = client.actor("reventadata/supercharge-scraper").call(run_input=run_input)
if run is None or run.status != "SUCCEEDED":
status = run.status if run else "unknown"
raise SystemExit(f"Run did not succeed (status: {status}).")
# 4. Fetch the saved sites
items = client.dataset(run.default_dataset_id).list_items().items
print(f"Retrieved {len(items)} sites.\n")
Behind the scenes:
- The Actor requests supercharge.info's full site list in one GET, with no proxy unless you set one.
- It applies your filters to that list locally.
- It saves the first
maxItemsmatches, in the order the source lists them. A run that saves no rows ends SUCCEEDED when the download loaded normally but nothing matched, and FAILED only if the download was unreadable or blocked.
Step 5: View and download results
Loop over the rows to print the parts you need:
for item in items:
print(item["name"])
print(f" Status: {item.get('status')}")
print(f" Stalls: {item.get('stallCount')} Max power: {item.get('powerKilowatt')} kW")
print(f" Plugs: {item.get('plugs')}")
print(f" Open to non-Tesla EVs: {item.get('otherEVs')}")
print(f" Map: {item.get('googleMapsUrl')}")
print("-" * 40)
Here is one row, a sample from a capture dated 2026-10-12, parsed with the Actor's own code. It is an open Spanish site with 12 stalls, so it passes the filters above. A saved row also carries scrapedAt, added at save time:
{
"id": 8734,
"name": "Rivas Vaciamadrid, Spain",
"url": "https://supercharge.info/map?siteID=8734",
"status": "OPEN",
"statusDays": 0,
"address": "C. Marie Curie, 4",
"city": "Rivas-Vaciamadrid",
"state": "Madrid",
"postalCode": "28521",
"country": "Spain",
"region": "Europe",
"latitude": 40.335907,
"longitude": -3.524279,
"elevationMeters": 564,
"stallCount": 12,
"powerKilowatt": 250,
"stalls": {
"v4": 12,
"accessible": 2,
"trailerFriendly": 5
},
"plugs": {
"ccs2": 12
},
"solarCanopy": false,
"battery": false,
"otherEVs": true,
"facilityName": null,
"dateOpened": "2025-03-27",
"locationId": "32352",
"countedInTotals": true,
"hours": null,
"facilityHours": null,
"accessNotes": null,
"addressNotes": "C.C. H2Ocio",
"plugshareId": 742461,
"osmId": 12590063608,
"parkingId": null,
"parkingType": null,
"googleMapsUrl": "https://www.google.com/maps/search/?api=1&query=40.335907%2C-3.524279",
"plugshareUrl": "https://www.plugshare.com/location/742461",
"osmUrl": "https://www.openstreetmap.org/node/12590063608",
"teslaFindUsUrl": "https://www.tesla.com/findus?location=32352",
"forumUrl": "https://supercharge.info/service/supercharge/discuss?siteId=8734"
}
Apify datasets export as JSON, CSV, XML, Excel, HTML table, RSS or JSONL.
Understanding your results
The fields that matter most:
-
statusandstatusDays: the site's status as the source gives it, and days in that status. A closed site in the capture,Kozina, Slovenia (old), hadCLOSED_PERMand 2200 days. -
stallCountandpowerKilowatt: total stalls and maximum site power. In the five captured sites, power was 250, 150 or 72 kW. -
stalls: stall types (v2,v3,v4,urban) plusaccessibleandtrailerFriendlycounts. The Spanish row above has 12 V4 stalls, 2 of them accessible and 5 trailer-friendly. -
plugs: plug standards (nacs,ccs1,ccs2,tpc,gbt,type2,multi). The two US sites in the capture hadtpconly. -
otherEVs: true when non-Tesla cars can charge. 2 of the 5 captured sites had it. -
hours,accessNotes,addressNotes,parkingType: free text and parking type from the source; null when absent. -
countedInTotals: whether the source counts the site in its network totals.
Values are passed through as the source's contributors maintain them; the README notes supercharge.info is a community tracker, not a Tesla source.
Pro tips
-
Pull the whole world in one run. The Actor downloads the full list on every run anyway, so
countryandregiononly decide what is saved. Setstatuses: []and a highmaxItemsfor an unfiltered export. -
Track the build pipeline.
CONSTRUCTION,PERMITandPLANare sites being built, permitted or announced. Rerun on a schedule and comparestatusperidbetween datasets to see sites move. -
Join on the cross-reference ids.
plugshareIdandosmIdlet you match rows to PlugShare or OpenStreetMap data you already hold;idis stable per site, andurlopens it on the supercharge.info map. -
Know what "unknown" does.
minStallsandminPowerKwkeep sites whose count or power is missing. If you need a hard floor, also drop rows wherestallCountis null in your own code. -
Start small. Keep
maxItemslow until your downstream code handles the nestedstallsandplugsobjects, then raise it.
Troubleshooting
-
Zero rows: a run that matches no site ends SUCCEEDED with an empty dataset; it ends FAILED only if the download was unreadable or blocked. Check
countryandregionspelling: the match is on the whole name, ignoring case. -
Run stops with an error about statuses: one of
statusesis not among the eight known values. -
Only open sites came back:
statuseswas left unset, which meansOPENonly. Pass[]for every status. -
Fewer rows than expected: read the log.
Filters matched N site(s).says how many passed, beforemaxItemscut. From Python:client.log(run.id).get().
For help, use the Actor's Store page.
Pricing and next steps
For current pricing and details, see the Store page.
To repeat an export, save the input as a task and add an Apify schedule, which runs an Actor or task on a cron expression (0 8 * * * is every day at 8 AM). To react when a run finishes, add a webhook on ACTOR.RUN.SUCCEEDED; its payload carries the run object, including defaultDatasetId.
Which countries or build stages are you planning to track? Drop a comment! 💬
Top comments (0)