Degree Days

Degree Days

Weather Data for Energy Saving

Degree XML API

The XML API is carefully designed and well tested. But it's not particularly easy to use. The wide range of options and data that can be specified and returned in XML means there's a lot of XML creation and parsing involved. There's also a strict HMAC-signature-based security scheme to negotiate.

We have robust client libraries for Java, .NET, and Python; we recommend you use them if you are using Java (or Kotlin, Scala, Groovy etc.), .NET (e.g. C# or VB.NET), or Python. All 3 client libraries use the XML API internally, but they make all the request options immediately available, and use fast XML-stream parsing to convert the XML responses into a simple object model. With the client libraries you can be up and running fetching data in a couple of minutes.

For dynamic object-oriented languages with good JSON support, the JSON API is typically easier to use than the XML API, as the JSON maps naturally and automatically to objects. We have code samples for JavaScript, Node.js, Office/Excel, PHP, PowerShell, R, Ruby, and VBA. But there is no other significant difference between the XML API and the JSON API. Both let you do exactly the same things, both have the same security scheme, both are served by the same backend system, and you can use either or both through the same API account. We suggest you choose whichever best suits your platform and experience.

If you do choose the XML API, the XML API test tool should be useful for testing different XML requests and seeing the XML responses that come back.

Using the XML API

Each of these steps is explained below:

1. Create the XML request

There are currently three main types of request:

All three types of request are highly configurable. The request formats of LocationDataRequest and LocationInfoRequest differ only in the name of the request (i.e. "LocationDataRequest" or "LocationInfoRequest"). The docs on the API's regression functionality explain how to configure an XML RegressionRequest. All XML requests are sent to the API in the same way explained further down on this page.

Below is an example XML request (a LocationDataRequest) that shows a few of the main options. You can copy/paste it into the XML API test tool to try it out. 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 at

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

    <!-- "Endpoint" is the URL you send your request to.  You can send it to (to call the API over HTTP) or (to call it over HTTPS); just make sure the
    Endpoint you specify here is exactly the same as the URL you actually send
    your request to. -->
    <!-- "AccountKey" comes with your API account. -->
    <!-- "Timestamp" should be the time of the request, in ISO format.
    You should update this for each request you make. -->
    <!-- "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 120 "DataSpec"
    items in a single request (e.g. HDD and CDD in 60 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 down this
          page. -->
      <!-- The following specifies CDD with a base temperature of 21.5 C, broken
      down monthly, and covering June, July, and August of 2023. -->
      <DatedDataSpec key="monthlyCDD">
            <DayRange first="2023-06-01" last="2023-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 2019, 2020, 2021, 2022, and 2023.
      NB as this covers 5 years it will use more request units than the
      much-shorter items defined above.  For repeated testing you might want to
      remove it or temporarily reduce its "numberOfValues" to 1 if your API
      account does not have a high rate limit. -->
      <AverageDataSpec key="averageHDD">
      <!-- The following specifies hourly temperatures for just the most recent
      full day.  You can fetch hourly temperatures covering much longer periods,
      but here we specify just one day to keep the example response short.
      NB fetching hourly temperatures uses extra request units and requires at
      least an API Standard account.  So we suggest you remove this
      TimeSeriesDataSpec from your code if you don't need it. -->
      <TimeSeriesDataSpec key="hourlyTemperatures">
        <!-- Hourly data is always broken down hourly, but TimeSeriesDataSpec
        takes a DatedBreakdown (like DailyBreakdown) to enable you to get hourly
        data that matches your degree days exactly, and so you can easily
        specify hourly data covering e.g. the last x days, weeks, or months.
        It is a little confusing at first, but it is useful too!
        NB If you want figures for the current day so far, add an
        allowPartialLatest="true" attribute to the DailyBreakdown element
        below: -->

Configuring your own LocationDataRequest

The above XML is just an example, you can easily configure your own LocationDataRequest to specify exactly the data you want. The XML API test tool can be useful here: you can copy/paste in the example XML request above and then modify it to try out the options described below:

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 is the postal/zip code of the location and must match
  [- 0-9a-zA-Z]{1,16} -->
  <PostalCode>WC2N 5DN</PostalCode>
  <!-- the CountryCode should be the 2-letter ISO country code of the location,
  in upper case e.g. US for United States, GB for Great Britain -->

A DataSpec can be either a DatedDataSpec (for daily/weekly/monthly/yearly degree days), an AverageDataSpec, or a TimeSeriesDataSpec (for hourly temperature data). All three are shown in the example request XML further above.

A Calculation (which goes inside a DatedDataSpec or AverageDataSpec) can specify heating or cooling degree days:



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

A TimeSeriesCalculation (which goes inside a TimeSeriesDataSpec) can specify hourly temperature data in Celsius or Fahrenheit:



A DatedDataSpec (and a TimeSeriesDataSpec*) 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 starting on the first day of each month.  The ---DD format is XML
Schema's gDay format for days that recur each month, based on ISO 8601. -->
<MonthlyBreakdown startOfMonth="---01">
  <!-- Period goes here. -->

<!-- startOfYear is optional, the default being --01-01 for regular calendar
years starting on Jan 1st each year.  The --MM-DD format is XML Schema's
gMonthDay format for days that recur each year, based on ISO 8601.
The example below specifies years starting on May 21st: -->
<YearlyBreakdown startOfYear="--05-21">
  <!-- Period goes here. -->

    <DayRange first="2023-10-16" last="2023-11-14"/>
    <!-- Typically one range would start the day after the last day of the
    previous range (i.e. no gap).  But you can have gaps between ranges if you
    like (e.g. if your energy data has gaps), as in this example which leaves a
    gap between 2023-11-15 and 2023-11-20. -->
    <DayRange first="2023-11-21" last="2023-12-17"/>

*As mentioned in the example XML above, it might seem strange that a TimeSeriesDataSpec (for hourly temperature data) would take these breakdowns in the same way that a DatedDataSpec (for degree days) does, but it works this way to give you flexibility in how you specify the time-period that the hourly data should cover, and to make it easy for you to get hourly data that lines up perfectly with your degree days.

All the breakdown types above can also have an allowPartialLatest attribute that you can specify as true (as opposed to the default value of false) to enable you to fetch time-series data that includes figures for the current day/week/month/year so far. For example:

<DailyBreakdown allowPartialLatest="true">
    <!-- Period goes here. -->

Please note that the most recent time-series data can be a little volatile, as weather stations sometimes send multiple reports for the same time, some delayed, and some marked as corrections for reports they sent earlier. Our system generates time-series data using all the relevant reports that each weather station has sent, but the generated figures may change if delayed or corrected reports come through later. If you are storing partial-latest time-series data we suggest you overwrite it later with figures generated after the day has completed and any delayed/corrected reports have had time to filter through.

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="2019-01-01" last="2023-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="2021-01-01" last="2023-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.

If you try this out in the XML API test tool, you will see that 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.

RegressionRequest for advanced regression functionality

With RegressionRequest you can send energy data to the API so it can test thousands of regressions and find the HDD and/or CDD base temperatures that give the best statistical fit. Our docs on the API's regression functionality cover all the RegressionRequest configuration options and the data you can expect in the RegressionResponse. They do not, however, cover how to send a RegressionRequest to the API and get a response back. This process is the same for all request types, so we suggest you follow the instructions on this page to get your code sending LocationDataRequest to the API and getting a response back, then get it sending RegressionRequest as well.

You can also test a RegressionRequest quickly with the API regression docs and the XML API test tool.

Back to overview

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

You need to send five parameters to the endpoint URL ( or

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 best to remove them.

If your base-64 function takes a byte array instead of a string, to encode the request XML you should 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, email us at 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.

Debugging your HTTP requests

The XML API test tool can be useful for debugging your HTTP requests, as it generates and shows all the HTTP request parameters for any XML request you give it. Usually the test tool's "Auto-prepare request" option is helpful, but you should switch it off if you are checking your encoded_request or encoded_signature against the ones generated by the test tool, as you don't want it modifying your XML request before generating the HTTP parameters.

Back to overview

3. Parse the XML response

Below is an annotated 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 that 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 will be 0 if you specified a StationIdLocation in your
          request.) -->
      <!-- Every DataSet has a key that matches the corresponding DataSpec from
      the XML request. 
      The following examples show the formats you can expect for the different
      types of DataSet: -->
      <DatedDataSet key="dailyHDD">
          <!-- d is the first day of the period that the value V covers.
          The ld (last day) property isn't included for daily data as it's
          always the same as d.
          We don't like cryptic abbreviations or attributes that are only
          sometimes present, but big responses can easily have hundreds of
          thousands of values, so we keep them small to minimize bandwidth and
          memory footprint. -->
          <V d="2024-04-07">3.8</V>
          <!-- pe is short for "percentage estimated".  If it's missing, it's
          the default value of 0. -->
          <V d="2024-04-08" pe="0.6">2.6</V>
          <V d="2024-04-09">1.4</V>
          <V d="2024-04-10">0.3</V>
          <V d="2024-04-11">2.9</V>
          <V d="2024-04-12" pe="1">3.9</V>
          <V d="2024-04-13">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="2023-06-01" ld="2023-06-30" pe="2">17.4</V>
          <V d="2023-07-01" ld="2023-07-31">73.6</V>
          <V d="2023-08-01" ld="2023-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>
      <TimeSeriesDataSet key="hourlyTemperatures">
          <!-- "dt" is short for "date-time".  They are in ISO format, so you
          can easily parse out the local time (from the first 16 characters),
          and the time-zone offset is there too if you want it or the time in
          UTC. -->
          <V dt="2024-04-13T00:00-04:00">5</V>
          <V dt="2024-04-13T01:00-04:00">4.8</V>
          <V dt="2024-04-13T02:00-04:00">3.5</V>
          <V dt="2024-04-13T03:00-04:00">2</V>
          <V dt="2024-04-13T04:00-04:00">1.8</V>
          <V dt="2024-04-13T05:00-04:00">1.2</V>
          <V dt="2024-04-13T06:00-04:00">2.2</V>
          <V dt="2024-04-13T07:00-04:00" pe="1">4.2</V>
          <V dt="2024-04-13T08:00-04:00" pe="1">8.5</V>
          <V dt="2024-04-13T09:00-04:00">11</V>
          <V dt="2024-04-13T10:00-04:00">11.5</V>
          <V dt="2024-04-13T11:00-04:00">12.8</V>
          <V dt="2024-04-13T12:00-04:00">12</V>
          <V dt="2024-04-13T13:00-04:00">12.2</V>
          <V dt="2024-04-13T14:00-04:00" pe="1">12.6</V>
          <V dt="2024-04-13T15:00-04:00" pe="2">11</V>
          <V dt="2024-04-13T16:00-04:00" pe="2">10.5</V>
          <V dt="2024-04-13T17:00-04:00">9.9</V>
          <V dt="2024-04-13T18:00-04:00">7.9</V>
          <V dt="2024-04-13T19:00-04:00" pe="1">7.2</V>
          <V dt="2024-04-13T20:00-04:00">6.9</V>
          <V dt="2024-04-13T21:00-04:00">6.9</V>
          <V dt="2024-04-13T22:00-04:00">6.4</V>
          <V dt="2024-04-13T23:00-04:00">6</V>

You can generate a similar response yourself by running the example XML request further above through the XML API test tool. But please note that the annotated response above is just an example to show the response structure; the figures you get in your live response will be different.

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:

  <!-- Note how the Metadata comes through as usual even though the request
  failed.  This is expected, you can rely on the Metadata being there. -->
  <!-- Note how there's a Failure instead of the LocationDataResponse or
  LocationInfoResponse you'd usually expect. -->
    <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 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 its keyed DataSpec/DataSet items can 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 omitted for clarity -->
      <!-- details omitted for clarity -->
      <!-- successful DataSet items omitted 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 omitted 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 request-failure possibilities), and you should be able to re-use most of your parsing code.

You can try this out with the XML API test tool by copy/pasting in the example LocationDataRequest further above, changing "<LocationDataRequest>" to "<LocationInfoRequest>" and "</LocationDataRequest>" to "</LocationInfoRequest>", and sending it to the API to get a LocationInfoResponse back.

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 API Plan and Start Today!

© 2008–2024 BizEE Software – About | Contact | Privacy | Free Website | API | Integration Guide | API FAQ | API Sign-Up