How it will work: 3 steps
- Follow us on ApifyThe tool is built and waits in the queue for the Apify Store. Apify is the cloud platform where our tools run.
- Paste your input and press StartNo code needed: you fill in a form (the input). It already has a working example. For example:
[{"name": "Munich warehouse roof", "lat": 48.137, "lon"…in Sites. Each start is one run. - Download the resultsWhen the run ends, the results are in a table (the dataset). Open Output, press Export and pick Excel or CSV. JSON and the API are there too.
This tool is an Apify Actor: a small program that runs on Apify. You can also run it on a schedule, for example every Monday. New to it? Read how it works, step by step and the 8 words you need first.
What it does
Solar PV Yield Estimator (PVGIS) estimates how much electricity a solar PV system will produce at each site in your list, using the official EU PVGIS 5.3 model from the European Commission's Joint Research Centre. For every site you get kWh per year, kWh per kWp, kWh per month, the optimal tilt and azimuth, a loss breakdown, year-to-year variability and an optional value estimate with your own tariff.
- Yield per site.
annualEnergyKwh,specificYieldKwhPerKwp,averageDailyEnergyKwhand 12 monthly values (monthlyEnergyKwhandmonthlywith irradiation and standard deviation). - Optimal angles. Leave out the tilt and azimuth and PVGIS finds the best values for the site. Give only the azimuth (for example a fixed roof direction) and PVGIS finds the best tilt.
- Both azimuth conventions. Enter azimuths the PVGIS way (0 = south) or as a compass bearing (180 = south). Every row has both
azimuthDegandazimuthCompassDeg. - Quality figures. In-plane irradiation, performance ratio, angle-of-incidence, spectral and temperature losses, total loss and year-to-year variability.
- Value estimate. Add your own price per kWh and each site gets
estimatedAnnualValue. - Clear status. Each row is
complete,not_found(sea or outside the radiation data) orerror, with a reason. Invalid sites are reported and skipped; the rest of the list still runs.
Good for
- Quick yield and kWh/kWp figures for a list of quotes or leads.
- Comparing orientations or tilts on the same site (one row per variant).
- Screening many locations for a solar portfolio.
- Feeding monthly kWh into your own sizing, self-consumption or ROI sheet.
Not a good fit
- Roof detection, panel layout or near shading from buildings and trees. PVGIS only uses the terrain horizon.
- Trackers, off-grid systems with batteries, or hourly time series.
- Tariffs, feed-in rules, taxes, degradation or payback. The value estimate is only kWh × your price.
Who uses it
Solar installers
Put a yield number into a customer quote fast.
Project developers
Screen and rank a long list of sites.
Home owners
Compare two roof directions before you buy.
Price
Prices will be shown after release.
Questions
Is it the same as the PVGIS website?
Yes, for the same inputs. The tool calls the PVGIS 5.3 API and keeps its values.
How exact are the numbers?
They are long-term averages. Real years vary, and shade, snow or dirt can lower output.
Can I use it outside Europe?
Yes. With the auto setting, sites outside the main data set use another database.
Do I need an API key?
No. PVGIS is free and open.
How accurate is this?
It is the same model as the PVGIS website, using long-term satellite or reanalysis data. The figures are averages: single years vary (see annualVariabilityPercent). Real systems also lose output to near shading, snow, soiling and equipment faults that PVGIS does not know about.
What should I use for lossPercent?
PVGIS suggests 14% for a typical system. Use more for long cable runs, old inverters or dusty sites.
Details
Step by step on Apify
- Open the Input tab.
- Keep the two example sites in
sites, or replace them with yours:lat,lonandpeakPowerKwfor each. - Keep Optimize missing angles on, or add
angleandazimuthto a site. - Click Start.
- Open the Output tab (Dataset) and check
annualEnergyKwhandspecificYieldKwhPerKwp.
Use cases
| Task | Input | Key fields | Next step |
|---|---|---|---|
| Yield for a quote | 1 site with lat, lon, peakPowerKw | annualEnergyKwh, monthlyEnergyKwh | Copy into the offer |
| Compare roof directions | Same site twice, different azimuth and angle | annualEnergyKwh, specificYieldKwhPerKwp | Pick the better layout |
| Screen a site list | Up to 100 sites with name | specificYieldKwhPerKwp, status | Rank or filter in a sheet |
| Rough revenue | Sites + tariffPerKwh + currency | estimatedAnnualValue | Your own ROI model |
Tips for better results
- Leave tilt and azimuth empty and PVGIS finds the best angles.
- Start with 14% system loss, as PVGIS suggests. Use more for old or dusty systems.
- Use the source link in each row to repeat the same request on PVGIS.
Limits and good to know
- 1 to 100 sites per run. For longer lists, use several runs
- Sites are computed 3 at a time; 100 sites took about 47 seconds in tests. The internal time budget is 240 seconds; sites left after that get
DEADLINE_REACHEDand are not charged - Fixed (non-tracking), grid-connected systems only. Monthly and yearly averages only, no hourly series
- PVGIS-SARAH3 covers Europe, Africa and parts of Asia and South America.
autofalls back to PVGIS-ERA5 elsewhere (in tests: New York, Tokyo, Sydney). Choosing a database that does not cover a site givesnot_found - The terrain horizon includes hills and mountains, not nearby buildings or trees
- If PVGIS fails 3 times in a row (outage, rate limit or timeout), the Actor stops calling it and marks the rest as not computed, so a source outage does not use up your run time
Troubleshooting
| Symptom | Check | Action |
|---|---|---|
not_found, LOCATION_OVER_SEA | latitude, longitude | Check the coordinates are on land and lat/lon are not swapped. |
not_found, OUTSIDE_DATABASE_COVERAGE | radiationDatabase input | Use auto or PVGIS-ERA5. |
error, INVALID_SITE | statusReason | Fix the field named in the reason. |
INVALID_INPUT | RUN_SUMMARY.message | Add at least one site, keep sites at 100 or fewer, check tariffPerKwh and currency. |
CAPPED | RUN_SUMMARY.message | Raise the maximum cost per run, or run the remaining sites again. |
ERROR or SOURCE_UNAVAILABLE rows | RUN_SUMMARY.http.failures | PVGIS was down or slow. Run the failed sites again later. |
| Yield looks too low | tiltDeg, azimuthCompassDeg, lossPercent | Check the azimuth convention: in pvgis, 0 is south; in compass, 0 is north. |
For developers
Sample result
One result from a real test run, in JSON. Real values change from run to run. In Excel or CSV each field is a column.
{
"siteIndex": 0,
"siteName": "Munich warehouse roof",
"status": "complete",
"statusReason": null,
"errorCode": null,
"latitude": 48.137,
"longitude": 11.575,
"elevationM": 524,
"peakPowerKw": 5,
"systemLossPercent": 14,
"mounting": "free-standing",
"pvTechnology": "c-Si",
"tiltDeg": 39,
"tiltOptimized": true,
"azimuthDeg": -1,
"azimuthCompassDeg": 179,
"azimuthOptimized": true,
"annualEnergyKwh": 5694.23,
"specificYieldKwhPerKwp": 1138.85,
"averageDailyEnergyKwh": 15.6,
"annualIrradiationKwhPerM2": 1432.61,
"performanceRatioPercent": 79.5,
"annualEnergyStdDevKwh": 312.43,
"annualVariabilityPercent": 5.49,
"angleOfIncidenceLossPercent": -2.87,
"spectralLossPercent": 1.49,
"temperatureIrradianceLossPercent": -6.23,
"totalLossPercent": -20.51,
"monthlyEnergyKwh": [257.21, 361.62, 520.03, 599.39, 600.6, 620.34, 641.43, 612.28, 532.09, 428.19, 274.69, 246.35],
"monthly": [
{ "month": 1, "monthName": "January", "energyKwh": 257.21, "dailyEnergyKwh": 8.3, "irradiationKwhPerM2": 59.91, "dailyIrradiationKwhPerM2": 1.93, "energyStdDevKwh": 58.14 }
],
"tariffPerKwh": null,
"currency": null,
"estimatedAnnualValue": null,
"radiationDatabase": "PVGIS-SARAH3",
"meteoDatabase": "ERA5",
"dataYearsFrom": 2005,
"dataYearsTo": 2023,
"horizonUsed": true,
"horizonDatabase": "DEM-calculated",
"pvgisVersion": "v5_3",
"sourceUrl": "https://re.jrc.ec.europa.eu/api/v5_3/PVcalc?lat=48.137&lon=11.575&peakpower=5&loss=14&mountingplace=free&pvtechchoice=crystSi&usehorizon=1&outputformat=json&optimalangles=1",
"fetchedAt": "2026-10-12T13:28:33.393Z",
"input": { "name": "Munich warehouse roof", "lat": 48.137, "lon": 11.575, "peakPowerKw": 5, "lossPercent": 14 }
}Data fields (40)
| Field | Type | Meaning |
|---|---|---|
siteIndex | integer | Position of the site in your input list (0-based). |
siteName | string | Site name from your input, or 'Site N' when not given. |
status | string | complete = estimate returned; not_found = PVGIS has no data here (sea, outside the radiation database); error = invalid site, source failure or not computed (see errorCode). |
statusReason | string | Plain-English reason for not_found or error rows; null when complete. |
errorCode | string | Machine code: INVALID_SITE, LOCATION_OVER_SEA, OUTSIDE_DATABASE_COVERAGE, SOURCE_BAD_REQUEST, SOURCE_UNAVAILABLE, RATE_LIMITED, CHARGE_LIMIT_REACHED, DEADLINE_REACHED, CANCELLED; null when complete. |
latitude | number | Latitude used, decimal degrees. |
longitude | number | Longitude used, decimal degrees. |
elevationM | number | Terrain elevation reported by PVGIS, metres. |
peakPowerKw | number | Installed peak PV power, kWp. |
systemLossPercent | number | System losses you entered (cables, inverter, soiling), %. |
mounting | string | free-standing or building-integrated (as used by PVGIS). |
pvTechnology | string | PV module technology used by PVGIS, e.g. c-Si, CIS, CdTe. |
tiltDeg | number | Module tilt from horizontal, degrees (as used by PVGIS, including optimized values). |
tiltOptimized | boolean | True when PVGIS chose the optimal tilt. |
azimuthDeg | number | Orientation in the PVGIS convention: 0 = south, -90 = east, 90 = west, ±180 = north. |
azimuthCompassDeg | number | Same orientation as a compass bearing: 0 = north, 90 = east, 180 = south, 270 = west. |
azimuthOptimized | boolean | True when PVGIS chose the optimal azimuth. |
annualEnergyKwh | number | Average annual AC energy output of the system, kWh/year (PVGIS E_y). |
specificYieldKwhPerKwp | number | Annual energy per installed kWp, kWh/kWp/year (annualEnergyKwh / peakPowerKw). |
averageDailyEnergyKwh | number | Average daily energy output, kWh/day (PVGIS E_d). |
annualIrradiationKwhPerM2 | number | Annual global irradiation on the module plane, kWh/m²/year (PVGIS H(i)_y). |
performanceRatioPercent | number | Derived performance ratio: specific yield / in-plane irradiation × 100. |
annualEnergyStdDevKwh | number | Year-to-year standard deviation of annual output, kWh (PVGIS SD_y). |
annualVariabilityPercent | number | Year-to-year variability: annualEnergyStdDevKwh / annualEnergyKwh × 100. |
angleOfIncidenceLossPercent | number | Angle-of-incidence (reflection) loss, % (PVGIS l_aoi; negative = loss). |
spectralLossPercent | number | Spectral effect, % (PVGIS l_spec; can be a gain or a loss); null when PVGIS does not report it. |
temperatureIrradianceLossPercent | number | Temperature and low-irradiance loss, % (PVGIS l_tg). |
totalLossPercent | number | Total loss including your system loss, % (PVGIS l_total). |
monthlyEnergyKwh | array | 12 values: average energy output per month, January to December, kWh (PVGIS E_m). Empty when not complete. |
monthly | array | 12 objects (month, monthName, energyKwh, dailyEnergyKwh, irradiationKwhPerM2, dailyIrradiationKwhPerM2, energyStdDevKwh). Empty when not complete. |
tariffPerKwh | number | Your price per kWh, if given. |
currency | string | Currency code for the value estimate, if a tariff was given. |
estimatedAnnualValue | number | annualEnergyKwh × tariffPerKwh. Simple model, not a financial forecast. |
radiationDatabase | string | Solar radiation database PVGIS used, e.g. PVGIS-SARAH3 or PVGIS-ERA5. |
meteoDatabase | string | Database for temperature and wind, e.g. ERA5. |
dataYearsFrom | integer | First year of the radiation data averaged by PVGIS. |
dataYearsTo | integer | Last year of the radiation data averaged by PVGIS. |
horizonUsed | boolean | True when terrain horizon shading was included. |
horizonDatabase | string | Source of horizon data, e.g. DEM-calculated. |
pvgisVersion | string | PVGIS API version, e.g. v5_3. |
Input settings (8)
The main fields of the input form. With the API you send them as JSON.
| Setting | Name in the API | Type | Example value |
|---|---|---|---|
| Sites required | sites | array | [{"name": "Munich warehouse roof", "lat": 48.137, "lon": 11.575, "peakPowerKw": 5, "los… |
| Optimize missing angles | optimizeAngles | boolean | true |
| Azimuth convention | azimuthConvention | string | "pvgis" |
| Radiation database | radiationDatabase | string | "auto" |
| PV module technology | pvTechnology | string | "crystSi" |
| Include horizon shading | useHorizon | boolean | true |
| Electricity value per kWh (optional) | tariffPerKwh | number | |
| Currency code | currency | string | "EUR" |
