Introduction
WattTime technology—based on real-time grid data, cutting-edge algorithms, and machine learning—provides first-of-its-kind insight into your local electricity grid’s marginal emissions rate.
The WattTime API provides access to real-time, forecast, and historical marginal emissions data for electric grids around the world. The marginal emissions rate we provide is a Marginal Operating Emissions Rate (MOER), in units of pounds of emissions per megawatt-hour (e.g. CO2 lbs/MWh). This MOER represents the emissions rate of the electricity generator(s) which are responding to changes in load on the local grid at a certain time. For a deeper understanding of how we produce marginal emissions rates, see our methodology page.
If you’re curious about what you can do with marginal emissions data, see our ‘what we do’ page, where you can dive into the various use cases. A common example of how to use the MOER value is to quantify the avoided emissions of a decision (manual or automated) to use energy at a different time than you otherwise would have (load-shifting). To quantify the pounds of CO2 emissions avoided by load-shifting, you would multiply your time-series of shifted energy usage (counterfactual MWh less actual MWh equals shifted MWh) by the MOER timeseries (CO2 lbs/MWh), then sum the result.
You can access the API by sending standard HTTP requests to the endpoints listed below. The /data
, /historical
, /forecast
, and /maps
endpoints are only available to subscribers. However, if you don’t yet have a subscription, you can preview all of the available region-specific data by providing CAISO_NORTH
as the ba
for your requests. A comparison of the ANALYST and PRO data plans can be found here.
Python3 example code is provided in the right pane of this documentation which shows how to interact with the API endpoints. For an example python script that pulls everything together, please see our example code on github.
Restrictions
There is a strict limit on the rate at which you may query the API. From any user, we allow a maximum of 3,000 requests in any 5-minute rolling window (an average of 10 requests per second). If requests exceed this, an HTTP 429
error code is returned.
API Status Page and User Alerts
WattTime publishes the current and historical uptime on the API Status Page. This page shows upcoming scheduled maintenance and provides updates during outages or maintenance.
Users should subscribe to alerts via the status page to be kept up to date. This page is our method of communicating updates to our users related to maintenance, outages, and announcements related to version upgrades. Follow these instructions to subscribe to alerts:
- Navigate to the WattTime API Status Page
- Click the “subscribe to updates” button in the top right corner
- Select your preferred means of notification (email, SMS, Slack, webhook)
- Enter your contact information (this will not be used for any other purpose)
Best Practices for API Usage
If using this API to control many smart devices in different locations, we suggest the following protocol. For each device location, query /ba-from-loc
to determine the ba
(the balancing authority serving the device). Then, query the other endpoints (e.g. /data
, /forecast
, etc.) with the resulting ba
to receive MOER data.
Because balancing authority region boundaries are occasionally updated, it is important to re-query /ba-from-loc
at least once a month to ensure devices are receiving the signal corresponding to their location.
Authentication
To start using the API, first register for an account by using the /register
endpoint. Then use the /login
endpoint to obtain an access token. You can then use your token to access the remainder of our endpoints. You must include your token in an authorization (bearer) header in subsequent requests to retrieve data. Your access token will expire after 30 minutes and you'll need to sign in again to obtain a new one.
Register New User
To register, use the code below. Please note that for these code examples we are using filler values for
username
(freddo),password
(the_frog),org
(freds world) and you should replace each if you are copying and pasting this code.
import requests
register_url = 'https://api2.watttime.org/v2/register'
params = {'username': 'freddo',
'password': 'the_frog',
'email': 'freddo@frog.org',
'org': 'freds world'}
rsp = requests.post(register_url, json=params)
print(rsp.text)
The above command returns JSON structured like this:
{
"user": "freddo",
"ok": "User created"
}
/register
Provide basic information to self register for an account.
HTTP Request
POST https://api2.watttime.org/v2/register
Query Parameters
Parameter | Description | Example | Type |
---|---|---|---|
username (required) | name of user that will be used in subsequent calls | freddo | string |
password (required) | user password. Password must be at least 8 characters, with at least 1 of each alpha, number and special characters. | the_frog | string |
email (required) | valid email address | freddo@frog.org | string |
org | organization name | freds world | string |
Login & Obtain Token
To login and obtain an access token, use this code:
import requests
from requests.auth import HTTPBasicAuth
login_url = 'https://api2.watttime.org/v2/login'
rsp = requests.get(login_url, auth=HTTPBasicAuth('freddo', 'the_frog'))
print(rsp.json())
The above command returns your personal token, in JSON structure like this:
{
"token": "abcdef0123456789fedcabc"
}
/login
Use HTTP basic auth to exchange username
and password
for an access token. Remember that you need to include this token in an authorization bearer header for all subsequent data calls. This header has the form: Authorization: Bearer <your_token>
HTTP Request
GET https://api2.watttime.org/v2/login
URL Query Parameters
Parameter | Description |
---|---|
none | uses basic authentication |
Response
Attribute | Description |
---|---|
token | abcdef0123456789fedcabc |
Password Reset
To reset your
password
, use this code:
import requests
password_url = 'https://api2.watttime.org/v2/password/?username=freddo'
rsp = requests.get(password_url)
print(rsp.json())
Sample Response:
{
"ok": "Please check your email for the password reset link"
}
/password
Provide your username
to request an email be sent to you with password reset instructions.
HTTP Request
GET https://api2.watttime.org/v2/password
URL Query Parameters
Parameter | Example | Type |
---|---|---|
username | freddo | string |
Response
Attribute | Description | Type |
---|---|---|
"ok" | Message confirming reset link has been sent | string |
Grid Emissions Information
Determine Grid Region
Make sure to replace the parameters
username
(e.g. ‘freddo’) andpassword
(e.g. ‘the_frog’) with your registered credentials when using this code. You should not add in your token here. The code automatically generates a new token each time you run it.
import requests
from requests.auth import HTTPBasicAuth
login_url = 'https://api2.watttime.org/v2/login'
token = requests.get(login_url, auth=HTTPBasicAuth(‘freddo’, ‘the_frog’)).json()['token']
region_url = 'https://api2.watttime.org/v2/ba-from-loc'
headers = {'Authorization': 'Bearer {}'.format(token)}
params = {'latitude': '42.372', 'longitude': '-72.519'}
rsp=requests.get(region_url, headers=headers, params=params)
print(rsp.text)
Sample Response
{
"id":169,
"abbrev":"ISONE_WCMA",
"name":"ISONE Western/Central Massachusetts"
}
/ba-from-loc
Emissions intensity varies by location, specifically the location where an energy-using device is interconnected to the grid. This endpoint, provided with latitude
and longitude
parameters, returns the details of the balancing authority (BA) serving that location, if known, or a Coordinates not found
error if the point lies outside of known/covered BAs. For more information on what a balancing authority is, see this explanation from the EIA.
HTTP Request
GET https://api2.watttime.org/v2/ba-from-loc
URL Query Parameters
Parameter | Description | Example | Type |
---|---|---|---|
latitude | Latitude of device location | 42.372 | float |
longitude | Longitude of device location | -72.519 | float |
Response
Parameter | Description | Example | Type |
---|---|---|---|
abbrev | Balancing authority abbreviation | ISONE_WCMA | string |
id | Unique WattTime id for the region | 169 | integer |
name | Human readable name/description for the region | ISONE Western/Central Massachusetts | string |
List of Grid Regions
import requests
from requests.auth import HTTPBasicAuth
login_url = 'https://api2.watttime.org/v2/login'
token = requests.get(login_url, auth=HTTPBasicAuth('freddo', 'the_frog')).json()['token']
list_url = 'https://api2.watttime.org/v2/ba-access'
headers = {'Authorization': 'Bearer {}'.format(token)}
params = {'all': 'false'}
rsp=requests.get(list_url, headers=headers, params=params)
print(rsp.text)
Sample Response
{
"ba": "CAISO_NORTH",
"name": "California ISO Northern",
"access": true,
"datatype": "MOER"
}
/ba-access
By default this endpoint delivers a list of regions to which you have access. Optionally, it can return a list of all grid regions where WattTime has data coverage. Access to this endpoint is available to all users.
HTTP Request
GET https://api2.watttime.org/v2/ba-access
URL Query Parameters
Parameter | Description | Example | Type |
---|---|---|---|
all | (Optional) If ‘all’: ‘true’ is specified, the entire list of regions will be returned. | true | Boolean |
Response
Parameter | Description | Example | Type |
---|---|---|---|
ba | Balancing authority abbreviation | CAISO_NORTH | string |
name | Human readable name for the region | California ISO Northern | String |
access | Boolean indicator of whether the requesting user has access to the particular grid region | true | Boolean |
datatype | Type of data used to check user’s access | MOER | string |
Real-time Emissions Index
import requests
from requests.auth import HTTPBasicAuth
login_url = 'https://api2.watttime.org/v2/login'
token = requests.get(login_url, auth=HTTPBasicAuth('freddo', 'the_frog')).json()['token']
index_url = 'https://api2.watttime.org/index'
headers = {'Authorization': 'Bearer {}'.format(token)}
params = {'ba': 'CAISO_NORTH'}
rsp=requests.get(index_url, headers=headers, params=params)
print(rsp.text)
Sample Response
{
"freq": "300",
"ba": "CAISO_NORTH",
"percent": "53",
"moer": "850.743982",
"point_time": "2019-01-29T14:55:00.00Z"
}
/index
Provides a real-time signal indicating the marginal carbon intensity for the local grid for the current time (updated every 5 minutes). This endpoint can return an index value, a MOER value, or both.
The index value (style: ‘percent’
) is the statistical percentile value of the current MOER relative to the last one month of MOER values for the specified location (100=dirtiest, 0=cleanest). This option is available to all users.
The MOER value (style: ‘moer’
) is the current MOER (e.g. CO2 lbs/MWh) for the specified location. This option is available only to users with PRO subscriptions. However, if you don’t yet have a subscription, you can preview the available data by providing CAISO_NORTH
as the ba
for your requests.
HTTP Request
GET https://api2.watttime.org/v2/index
URL Query Parameters
Parameter | Description | Example | Type |
---|---|---|---|
ba | Balancing authority abbreviation. (Optional: provide ba OR provide latitude +longitude , not all three) |
CAISO_NORTH | string |
latitude | Latitude of device location | 42.372 | float |
longitude | Longitude of device location | -72.519 | float |
style | (Optional) Units in which to provide realtime marginal emissions. Choices are 'percent', 'moer' or 'all'. If you have PRO access you may use 'moer' to get the latest raw MOER value. Defaults to 'all' if not provided | all | string |
Response
Parameter | Description | Example | Type |
---|---|---|---|
freq | Duration in seconds for which the data is valid from point_time |
300 | string |
ba | Balancing authority abbreviation | CAISO_NORTH | string |
percent | A percentile value between 0 (minimum MOER in the last month i.e. clean) and 100 (maximum MOER in the last month i.e. dirty) representing the relative realtime marginal emissions intensity. | 53 | stringified integer |
moer | Marginal Operating Emissions Rate (MOER) value measured in lbs/MWh. This is only available for PRO subscriptions. | 850.743 | stringified float |
point_time | ISO8601 UTC date/time format indicating when this data became valid | 2019-01-29T14:55:00.00Z | string |
Grid Emissions Data
import requests
from requests.auth import HTTPBasicAuth
login_url = 'https://api2.watttime.org/v2/login'
token = requests.get(login_url, auth=HTTPBasicAuth('freddo', 'the_frog')).json()['token']
data_url = 'https://api2.watttime.org/v2/data'
headers = {'Authorization': 'Bearer {}'.format(token)}
params = {'ba': 'CAISO_NORTH',
'starttime': '2022-11-16T20:30:00-0800',
'endtime': '2022-11-16T20:45:00-0800'}
rsp = requests.get(data_url, headers=headers, params=params)
print(rsp.text)
Sample Response
[
{"point_time": "2022-11-17T04:45:00.000Z", "value": 937.0, "frequency": 300, "market": "RTM", "ba": "CAISO_NORTH", "datatype": "MOER", "version": "3.2"},
{"point_time": "2022-11-17T04:40:00.000Z", "value": 937.0, "frequency": 300, "market": "RTM", "ba": "CAISO_NORTH", "datatype": "MOER", "version": "3.2"},
{"point_time": "2022-11-17T04:35:00.000Z", "value": 937.0, "frequency": 300, "market": "RTM", "ba": "CAISO_NORTH", "datatype": "MOER", "version": "3.2"},
{"point_time": "2022-11-17T04:30:00.000Z", "value": 937.0, "frequency": 300, "market": "RTM", "ba": "CAISO_NORTH", "datatype": "MOER", "version": "3.2"}
]
/data
Obtain historical MOERS (e.g. CO2 lbs/MWh) for a specified grid region (balancing authority abbreviated code, ba
) or location (latitude
& longitude
pair). By default, with none of the optional parameters, the response will contain the latest available value for the latest version of each available data type.
Access to this endpoint is restricted to customers with ANALYST or PRO subscriptions. However, if you don’t yet have a subscription, you can preview the available data by providing CAISO_NORTH
as the ba
for your requests.
HTTP Request
GET https://api2.watttime.org/v2/data
URL Query Parameters
Parameter | Description | Example | Type |
---|---|---|---|
ba | Balancing authority abbreviation (Optional: provide ba OR provide latitude +longitude , not all three) |
CAISO_NORTH | string |
latitude | Latitude of device location | 42.372 | float |
longitude | Longitude of device location | -72.519 | float |
starttime | (Optional)ISO 8601 timestamp (inclusive) - defines the first point_time of the batch. Must be provided in endtime is also provided. If omitted, the most recent data is returned. | 2022-11-16T20:30:00-0800 | string |
endtime | (Optional)ISO 8601 timestamp (inclusive) - if endtime is omitted, endtime is equal to the current time. | 2022-11-16T20:45:00-0800 | string |
moerversion | (Optional) MOER version. Defaults to the latest version for a given region if omitted. Use this field only to request a particular version. | 3.2 | string |
Response
Parameter | Description | Example | Type |
---|---|---|---|
ba | Balancing authority abbreviation | CAISO_NORTH | string |
datatype | Type of data | MOER | string |
frequency | Duration in seconds for which the data is valid from point_time |
300 | integer |
market | Disregard for MOER queries. This market type is only relevant for grid data that is not currently in use. | RTM | string |
point_time | ISO8601 UTC date/time format indicating when this data became valid | 2022-11-17T04:45:00.000Z | string |
value | Number value of the specified datatype above (e.g. Marginal Operating Emissions Rate (MOER) value measured in lbs/MWh) | 937.0 | float |
version | MOER version. This value can change over time as we update the data model. (Not present and not applicable for other datatypes) | 3.2 | string |
Historical Emissions
from os import path
import requests
from requests.auth import HTTPBasicAuth
login_url = 'https://api2.watttime.org/v2/login'
token = requests.get(login_url, auth=HTTPBasicAuth('freddo', 'the_frog')).json()['token']
historical_url = 'https://api2.watttime.org/v2/historical'
headers = {'Authorization': 'Bearer {}'.format(token)}
ba = 'CAISO_NORTH'
params = {'ba': ba}
rsp = requests.get(historical_url, headers=headers, params=params)
cur_dir = path.dirname(path.realpath('__file__'))
file_path = path.join(cur_dir, '{}_historical.zip'.format(ba))
with open(file_path, 'wb') as fp:
fp.write(rsp.content)
print('Wrote historical data for {} to {}'.format(ba, file_path))
Sample Response
Content-Type:application/zip
/historical
Obtain a zip file containing monthly .csv
files with the MOER values (e.g. CO2 lbs/MWh) and timestamps for a requested region for the past two years or more.
WattTime periodically updates the algorithms used to calculate new MOERs to add new features, incorporate new data sources, or in response to changing grid dynamics. This endpoint provides you with the option to retrieve all the MOER versions published for a given region or to retrieve only the most up-to-date version.
Historical data will be updated on the 2nd day of each month at midnight UTC for the previous month.
Access to this endpoint is restricted to customers with ANALYST or PRO subscriptions. However, if you don’t yet have a subscription, you can preview the available data by providing CAISO_NORTH
as the ba
for your requests.
HTTP Request
GET https://api2.watttime.org/v2/historical
URL Query Parameters
Parameter | Description | Example | Type |
---|---|---|---|
ba | Balancing authority abbreviation | CAISO_NORTH | string |
version | (Optional) Retrieve either the latest MOER version or all MOER versions ever published for this BA.. Provide ‘latest’ for the latest version or ‘all’ for all versions. Defaults to ‘latest’ if omitted. | all | string |
Response
Description | Type |
---|---|
A binary zip file payload containing monthly .csv files with MOERs for the specified balancing authority for the past two years. Save the body to disk and unzip it. |
Binary Data Zip File |
Emissions Forecast
import requests
from requests.auth import HTTPBasicAuth
login_url = 'https://api2.watttime.org/v2/login'
token = requests.get(login_url, auth=HTTPBasicAuth('freddo', 'the_frog')).json()['token']
forecast_url = 'https://api2.watttime.org/v2/forecast'
headers = {'Authorization': 'Bearer {}'.format(token)}
params = {'ba': 'CAISO_NORTH',
'extended_forecast': 'True'}
rsp = requests.get(forecast_url, headers=headers, params=params)
print(rsp.text)
Sample Response
[
{
"generated_at": "2022-12-19T18:50:00+00:00",
"forecast":
[
{
"point_time": "2022-12-19T18:55:00+00:00",
"ba": "CAISO_NORTH",
"value": 1048.4131919701972,
"version": "3.2-1.0.0"
},
{
"point_time": "2022-12-19T19:00:00+00:00",
"ba": "CAISO_NORTH",
"value": 1051.0880801214637,
"version": "3.2-1.0.0"},
...<72 hours worth of datapoints>...
]
}
]
/forecast
Obtain a forecast of the MOERs (e.g. CO2 lbs/MWh) for a specified region. Omitting the starttime
and endtime
parameters will return the most recently generated forecast. Use the starttime
and endtime
parameters to obtain historical forecast data.
Note that starttime
and endtime
define the time when a forecast was generated. Every five minutes, WattTime generates a new forecast which is 24 hours in duration (or 72 hours for the extended forecast). So, if you make a request to the /forecast
endpoint with starttime
of Jan 1, 1:00 and endtime
of Jan 1, 1:05, you will receive a set of forecast values (with 5-minute frequency) generated at 1:00 and 1:05, on January 1.
Access to this endpoint is restricted to customers with PRO subscriptions. However, if you don’t yet have a subscription, you can preview the available data by providing CAISO_NORTH
as the ba
for your requests.
HTTP Request
GET https://api2.watttime.org/v2/forecast
URL Query Parameters
Parameter | Description | Example | Type |
---|---|---|---|
ba | Balancing authority abbreviation | CAISO_NORTH | string |
starttime | (Optional) ISO 8601 timestamp (inclusive) - Omit to obtain the real-time forecast. If included, those forecasts generated between start and endtime are returned. | 2022-12-19T18:50:00+00:00 | string |
endtime | (Optional) ISO 8601 timestamp (inclusive)- Omit to obtain the real-time forecast. If included, those forecasts generated between start and endtime are returned. | 2022-12-19T18:55:00+00:00 | string |
extended_forecast | (Optional) Will provide a 72-hour forecast | true | Boolean |
Response
Attribute | Description | Example | Type |
---|---|---|---|
generated_at | ISO 8601 timestamp indicating when the forecast was generated | 2022-12-19T18:50:00+00:00 | string |
forecast | List of forecasts - see description below | object |
The value of the forecast
key is a list of dictionaries containing the following keys:
Key | Description | Example | Type |
---|---|---|---|
ba | Balancing authority abbreviation | CAISO_NORTH | string |
point_time | ISO8601 UTC date/time format indicating when this data became valid | 2022-12-19T18:55:00+00:00 | string |
value | Forecast value for the MOER measured in lbs/MWh. | 1048.4131919701972 | float |
version | MOER forecast version. This value can change over time as we update the forecast model. | 3.2-1.0.0 | string |
Grid Region Map Geometry
import requests
from requests.auth import HTTPBasicAuth
from os import path
login_url = 'https://api2.watttime.org/v2/login'
token = requests.get(login_url, auth=HTTPBasicAuth('freddo', 'the_frog')).json()['token']
maps_url = 'https://api2.watttime.org/v2/maps'
headers = {'Authorization': 'Bearer {}'.format(token)}
rsp=requests.get(maps_url, headers=headers)
cur_dir = path.dirname(path.realpath('__file__'))
file_path = path.join(cur_dir, 'wt_map.geojson')
with open(file_path, 'wb') as fp:
fp.write(rsp.content)
Sample Response
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"abbrev": "AECI",
"name": "Associated Electric Coop Inc"
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[-92.8982, 38.8797],
[-92.8850, 38.8585],
…<all coordinates for the polygon>...
]
]
]
}
}
…<all grid regions>...
],
"meta": {
"last_updated": "2022-02-17T00:57:00+00:00"
}
}
/maps
The /maps
endpoint provides a geojson of the grid region boundary for all regions that WattTime covers globally. Check when the geojson was last updated using the ‘last_updated’ value in the “meta” object of the geojson.
Access to this endpoint is restricted to customers with ANALYST or PRO subscriptions.
HTTP Request
GET https://api2.watttime.org/v2/maps
URL Query Parameters
None.
Response
A geojson response, that is a Feature Collection with properties that describe each BA, and multipolygon geometry made up of coordinates which define the boundary for each BA. The “meta” object contains the date-time that the geojson was last updated.
Technical Support
For technical questions related to this API and real-time emissions data, please contact support@WattTime.org
If you are experiencing an outage, please check the WattTime API Status Page to get the status of known outages, and if none are shown, please proceed to contact support@WattTime.org