Degree Days

Degree Days

Weather Data for Energy Professionals

Weather Underground

Degree XML API

At its core the Degree API is an XML API. The Java, .NET, and Python client libraries are wrappers around the XML API.

The XML API is carefully designed and well tested. However, unlike the client libraries, it is not particularly easy to develop against. It is built for performance rather than ease of development.

The XML API is not a simple REST API with a static URL for each "unit" of data. You can use it to fetch many years' worth of custom-specified data in a single request, in multiple base temperatures and breakdowns. The range of options and data that can be specified and returned in XML make it difficult to program against the XML API directly.

There is also an HMAC-signature-based security scheme to negotiate.

If at all possible, we recommend that you access the API using one of our client libraries. With the client libraries the API is really easy to use - you can literally be up and running fetching data in a couple of minutes. With the client libraries, all the advanced options for data-specification are readily available without extra effort.

At present we have robust client libraries for Java (and other languages that can be run on the JVM), .NET (e.g. C# and VB.NET), and Python.

We are planning to make more client libraries, but we are finding it difficult to decide which languages and platforms to prioritize. If the above options don't cater for your project particularly well, please email us at to let us know what language/platform you are using.

Using the XML API

Each of these steps is explained below:

1. Create the XML request

There are currently two main types of request:

Both types of request are highly configurable, and the only difference in the request format is in the name of the request (i.e. LocationDataRequest or LocationInfoRequest).

Below is an example XML request (a LocationDataRequest) that shows a few of the main options. Below that are more details of the various options you can use within the XML.

If you have a specific data-fetching pattern in mind, and you're struggling to figure out what XML you'll need, please feel free to email us for help on

<?xml version="1.0" encoding="UTF-8" ?>

        <!-- "Endpoint" is always the same.  It's the URL you send the request to. -->
        <!-- "AccountKey" comes with your API account. -->
        <!-- "Timestamp" should be the time of the request in ISO format. -->
        <!-- "Random" should be a random string, generated afresh for each request.
        It can be a random number or a UUID or similar. -->
        <!-- Specify the "Location" you want data for (more options further below):-->
        <!-- Now specify what data you want.  You can specify up to 100 "DataSpec"
        items in a single request (e.g. HDD and CDD in 50 base temperatures each).
        Give each "DataSpec" a "key" attribute so you can identify the corresponding
        "DataSet" in the response. -->
            <!-- The following specifies HDD with a base temperature of 65 F, broken
            down daily (a value for each day), and covering the last 7 days. -->
            <DatedDataSpec key="dailyHDD">
                <!-- The "Calculation" defines the type of degree days (e.g. heating or
                cooling) and the base temperature. -->
                <!-- There are different types of "Breakdown" (see further below). -->
                    <!-- A Breakdown has a "Period" to specify the dates that the data
                    should cover.  There's more on specifying the Period further below. -->
            <!-- The following specifies CDD with a base temperature of 21.5 C, broken 
            down monthly, and covering June, July, and August of 2017. -->
            <DatedDataSpec key="monthlyCDD">
                        <DayRange first="2017-06-01" last="2017-08-31"/>
            <!-- The following specifies 5-year-average HDD with a base temperature of
            15.5 C.  If you made this request today you could expect to get an average
            of data from 2013, 2014, 2015, 2016, and 2017. -->
            <AverageDataSpec key="averageHDD">

The Location can be a station ID, or a "geographic location" for which the API will select the best weather station to use automatically:

    <!-- The StationId must match [-_0-9a-zA-Z]{1,60} -->

    <LongLat longitude="-135.23127" latitude="43.92135"/>

    <!-- The PostalCode must match [- 0-9a-zA-Z]{1,16} -->
    <PostalCode>WC2N 5DN</PostalCode>
    <!-- The CountryCode should be a 2-letter ISO country code, in upper case. -->

A DataSpec can be either a DatedDataSpec (for daily/weekly/monthly/yearly data), or an AverageDataSpec. Both are shown by the example request XML above.

A Calculation can specify heating or cooling degree days:



All temperatures must be specified as whole numbers or with one digit after the decimal point.

A DatedDataSpec can have the following kinds of breakdown:

    <!-- Period goes here. -->

<WeeklyBreakdown firstDayOfWeek="Monday">
    <!-- Period goes here. -->

<!-- startOfMonth is optional, the default being ---01 for regular calendar months. -->
<MonthlyBreakdown startOfMonth="---01">
    <!-- Period goes here. -->

<!-- startOfYear is optional, the default being --01-01 for regular calendar years.
The example below specifies years starting on May 21st. -->
<YearlyBreakdown startOfYear="--05-21">
    <!-- Period goes here. -->

An AverageDataSpec can currently only have one type of breakdown:

<!-- This specifies that data should be averaged from the full calendar years
specified by the period. -->
    <!-- Period goes here.  Typically you'd use a LatestValuesPeriod to specify
    that the average should come from the most recent x full calendar years. -->

A Period can be specified in two ways:

<!-- Get the most recent available data: -->
    <!-- MinimumNumberOfValues is optional - you can specify it if you would
    rather have a failure than a partial set of data with less than your specified
    minimum number of values.  (Otherwise you may get back less data than you
    asked for if there aren't enough temperature-data records to generate a full
    set for your specified location.) -->

<!-- Get data covering your specified dates: -->
    <DayRange first="2013-01-01" last="2017-12-31"/>
    <!-- MinimumDayRange is optional - you can specify it if you would rather have
    a failure than a partial set of data covering less than your specified minimum
    range (like for MinimumNumberOfValues above). -->
    <MinimumDayRange first="2015-01-01" last="2017-12-31"/>

LocationInfoRequest and two-stage data fetching

Except in name, LocationInfoRequest looks exactly the same as LocationDataRequest. Take the example LocationDataRequest XML above, change "<LocationDataRequest>" to "<LocationInfoRequest>" and "</LocationDataRequest>" to "</LocationInfoRequest>", and you will have a valid LocationInfoRequest.

Request Units

Each API request you make uses request units that count against your hourly rate limit. A big LocationDataRequest can use a lot of request units, but a LocationInfoRequest will only ever use one. See the sign-up page for more on request units and rate limits.

But LocationInfoResponse does not contain any data (it has no <DataSets> element)... It's typically used for two-stage data fetching, which can be useful if you are dealing with geographic locations (postal/zip codes, or longitude/latitude positions), but storing data by station ID (returned in every successful response). For this use-case, two-stage data fetching can help you save request units (see right) and improve the efficiency of your system by avoiding re-fetching data that you already have stored.

When you want to add a new location into your system (e.g. if a new user signs up with a new address), you can do the following:

Two-stage fetching will only improve efficiency and save request units if/when you have enough geographic locations in your system that some of them end up sharing weather stations. But, if that is the case, two-stage fetching can really help your system to scale well as more and more geographic locations are added in.

Back to overview

2. Generate the HTTP parameters and send them to our servers

You need to send five parameters to the endpoint URL (

HTTP POST is ideal, but, if that's difficult for you, a GET should be OK too.

The signature and base64url encoding are both explained below:

The signature

The signature has two purposes:

The signature_method will ideally be HmacSHA256. If you can't do that for whatever reason (it might not be supported by your programming language), HmacSHA1 should work instead. Of course the method you use to make the signature has to correspond with the signature_method parameter that you send with your request.

Your XML string should be fed into the HMAC function, with the key being your security key (one of the access keys that comes with your API account). If the HMAC function requires a byte array, convert the XML string into bytes first using UTF-8 encoding. The HMAC function should return the signature as a byte array. Encode that using base64url (as described below), and use the resulting string as the encoded_signature parameter.

base64url encoding

base64url encoding is required to:

base64url encoding is like regular base 64, but with a few differences:

Many libraries can do base64url encoding automatically, but if not, then hopefully you can generate a regular base-64 string and convert it using the simple rules above. Note that you might find it works OK with the = characters and line breaks left in, but to ensure future compatibility it's probably best to remove them.

If your base-64 function takes a byte array, not a string, to encode the request XML you can encode the XML string into bytes first (using UTF-8 encoding) and then feed the bytes into your base-64 function.

If, in your programming language, you're struggling to generate anything base 64, drop us an email as we should be able to set up an alternative like hex.

Sending the parameters to the server

As mentioned above, an HTTP POST is the best way, but, if that's difficult in your programming language, a GET should work too.

If you can handle a compressed response, you can set the Accept-encoding header to gzip or deflate. Then the XML response will come back in compressed format. This won't make much of a difference if you're only fetching a small amount of data with each request, but it's well worth using compression to reduce bandwidth if you're fetching daily data covering long periods or in lots of base temperatures.

Back to overview

3. Parse the XML response

Below is an example XML response to the example XML LocationDataRequest above. It shows all the elements and attributes that you should see, apart from Failure elements (covered later), but, when writing your parsing code, please do bear in mind that we may add extra new elements or attributes in the future.

    <!-- The following Metadata is included with every response. -->
            <!-- RequestUnitsAvailable shows how many request units your account has
            left in the current period. -->
            <!-- MinutesToReset is the number of minutes until your available request
            units are reset to their full allowance. -->
    <!-- The LocationDataResponse element is what you get in response to a
    LocationDataRequest (from your XML request). -->
            <!-- There will always be a StationId, assuming your request could be
            satisfied. -->
            <!-- The TargetLocation will always have the same format, however you
            specified the Location in your XML request.  If you specified a
            StationIdLocation it will give the location of the station; if you
            specified a LongLatLocation it will repeat those coordinates back to you;
            if you specified a PostalCodeLocation (as in the example XML request) it
            will give you the coordinates that the API used to represent that postal
            code. -->
                <LongLat longitude="-70.59047" latitude="42.7455"/>
            <!-- The Sources represent the stations that were used to generate your
            data.  There will always be at least one, and at present there will only
            be one (we've allowed for the future possibility of combining data from
            multiple stations without breaking compatibility). -->
                        <LongLat longitude="-70.5215" latitude="41.6585"/>
                        <DisplayName>Otis Air National Guard Base, MA, US</DisplayName>
                    <!-- How far is this Station from the TargetLocation?  (It'll be 0 if
                    the TargetLocation was a StationIdLocation.) -->
            <!-- Every DataSet has a key that matches the corresponding DataSpec from
            the XML request. 
            The following examples show the different formats you can expect for 
            different types of DataSet. -->
            <DatedDataSet key="dailyHDD">
                    <V d="2018-03-16">3.8</V>
                    <!-- pe is short for percentage estimated.  If it's missing, it's the
                    default value of 0. -->
                    <V d="2018-03-15" pe="0.6">2.6</V>
                    <V d="2018-03-14">1.4</V>
                    <V d="2018-03-13">0.3</V>
                    <V d="2018-03-12">2.9</V>
                    <V d="2018-03-11" pe="1">3.9</V>
                    <V d="2018-03-10">1.4</V>
            <DatedDataSet key="monthlyCDD">
                    <!-- ld is short for last date.  It's only included if it's different
                    to d (the first date) i.e. if the data is weekly or monthly or yearly
                    instead of daily. -->
                    <V d="2017-06-01" ld="2017-06-30" pe="2">17.4</V>
                    <V d="2017-07-01" ld="2017-07-31">73.6</V>
                    <V d="2017-08-01" ld="2017-08-31">24.1</V>
            <AverageDataSet key="averageHDD">
                    <!-- Annual has the average-annual total. -->
                    <Annual pe="0.2">2478.3</Annual>
                        <!-- M (Month) no 1 has the average value for January; 2 has the
                        average value for February; etc. -->
                        <M no="1" pe="0.01">523.9</M>
                        <M no="2" pe="0.3">435</M>
                        <M no="3" pe="0.06">363.1</M>
                        <M no="4" pe="0.007">203.5</M>
                        <M no="5" pe="0.4">85.9</M>
                        <M no="6" pe="1">21.1</M>
                        <M no="7" pe="0.2">1.9</M>
                        <M no="8" pe="0.01">4.2</M>
                        <M no="9" pe="0.1">35.8</M>
                        <M no="10" pe="0.08">125.4</M>
                        <M no="11" pe="0.007">257.3</M>
                        <M no="12" pe="0.03">421.2</M>

Failures and failure codes

If something goes wrong, the XML response will contain a Failure.

Every Failure has a Code that indicates the cause of the failure.

These codes are named in a hierarchical way. For example, if a failure is caused by an invalid request, its code will begin with "InvalidRequest". The idea is that you can quickly test for broader types of failure code without having to know or itemize all the sub-types (like "InvalidRequestAccount" and "InvalidRequestSignature").

New codes may be added into the API at any time. New codes might be sub-types of existing types (like if "InvalidRequestSomeNewCode" was added as a sub-type of "InvalidRequest"), or they might be completely new (like "SomeCompletelyNewCode"). If you're writing logic that checks for different failure codes, make sure that it won't blow up if it comes across a code that it doesn't recognize.

Request failure

Any request can fail completely for a variety of reasons. Here's an example of the sort of XML response you should expect if it does:

        <Message>Sorry, we do not recognize the location that you specified. Our
        postal-code database did not recognize the specified PostalCodeLocation, and
        was consequently unable to find its longitude/latitude position.</Message>

Here are some failure codes that you might see:

Location failures (all codes starting with "Location"):

RateLimit failures (all codes starting with "RateLimit"):

InvalidRequest failures (all codes starting with "InvalidRequest"):

Service failures (all codes starting with "Service"):

The list above is not complete, and more failure codes may be added at any time. Instead of trying to get your system to handle them all, we suggest you just watch out for the codes you want to do something specific with. Always test for codes with e.g. code.startsWith("LocationNotRecognized") so that your system will be able to handle us adding new sub-types of the codes you're watching out for.

DataSet failure (a partial failure)

A LocationDataRequest can succeed partially, but some or all of the keyed DataSpec/DataSet items within it can still fail. For example, you might specify that you want data from 100 years ago from a specific PostalCodeLocation. The API might find a good weather station to match the postal code (partial success), but not one with coverage going that far back in time (partial failure).

In such instances, you can expect to receive a Failure element in place of the DataSet that you would usually receive if everything had worked. The Failure will have a key attribute matching the key of the corresponding DataSpec from your request.

        <!-- Details ommitted for clarity -->
            <!-- Details ommitted for clarity -->
            <!-- Successful DataSet items ommitted for clarity -->
            <Failure key="keyOfCorrespondingDataSpecFromRequest">
                <Message>Sorry, the source does not have enough recorded temperature
                readings for us to be able to generate data covering enough time to
                satisfy your specification.</Message>
            <!-- Successful DataSet items ommitted for clarity -->


If you submit a LocationInfoRequest you'll get a LocationInfoResponse back. LocationInfoResponse is basically just LocationDataResponse without the <DataSets> element. It contains the <Head> element only. And of course the XML says "<LocationInfoResponse>" instead of "<LocationDataResponse>".

Beyond these differences, LocationInfoResponse is identical to LocationDataResponse (including the same Failure possibilities), and you should be able to re-use most of your parsing code.

Back to overview

Higher-level integration help

This page focuses on the technical details, but is also worth reading the higher-level integration guide for tips on the various approaches to integrating with the API. We have helped a lot of businesses integrate their software with our API so we are very familiar with the patterns that work well for common use cases.

Choose your Plan and Sign Up Today!

© 2018 BizEE Software Limited - About | Contact | Web Tool | API | Integration Guide | API FAQ | API Sign-Up