Webperf's API

The API is under constant change – Work in Progress

More information and documentation is added continuously. Unless stated otherwise, responses are in JSON format. Want to see the OpenAPI/Swagger documentation? You'll find it here!

1. Open data

The data that Webperf releases as open data is published under the CC BY-SA 4.0 licence. This means you need to give proper attribution (that the data source is Webperf), and if you share the data source further, or adapt it, it must keep the same licence.

1.1 URLs to municipality and region websites

The URLs you get from this API endpoint are the addresses of municipality and region websites as stored at Webperf.se. Usually that is the start page, but a few municipalities use a sub-page as the entry point for residents, and then that is the address given here.
Note that Visby municipality in this list has been replaced by Region Gotland.

GET
https://api.webperf.se/public-sector/

The response contains the following data:

The point of URIs to Wikidata? Linked data!

"Linked data is a method of publishing structured data in a way that makes it possible to follow connections between pieces of information across multiple sources. Linked data builds on web standards such as HTTP and URIs, but instead of using them to present information to humans, the aim is to make data readable by machines. The goal is to enable widely differing actors to describe their information in a uniform way and thereby simplify reuse and interoperability."

So you can relate information at Webperf with Wikidata, which lets you fetch both even more data and also connect to various Wikipedia articles you may want to work with. This makes the data source machine-readable and processable automatically in your own application.

This can be handy if you want to automate fetching municipalities' names in other languages, which province they belong to, population size, their official email address, images from Wikimedia and everything else offered by Wikidata. See, for example, Wikidata's URI for Åmål municipality.

Code examples to get you started with Webperf's open data

On Webperf's GitHub account there are code examples you can reuse or take inspiration from.

1.2 Persistent URI (for municipalities and regions)

A URI is an identifier in the (linked) open data world. It is meant to be persistent and a unique reference that you may come across in datasets from Webperf. External parties are also free to use these URIs.
You'll find URIs in each listing and they can also be looked up. A URI for Webperf follows the syntax below and returns the public information that is available to share.

Do you have Premium? Then you can get your own data and the overall scores just by supplying an API key. See more about URIs for Premium.

GET
https://api.webperf.se/item/number

For example the following for Västra Götaland Region:

GET
https://api.webperf.se/item/141

The response contains the following data:

1.3 Test types

In several of the API's responses a field type_of_test appears – an integer that identifies which kind of test was run. This endpoint is an open catalogue of every test type in Webperf Core, with their id numbers and names. So you need no API key. The data source is Webperf Core and is released as open data.

Note that the names of the test types that are active in the current Webperf Core are taken verbatim from Webperf Core's own locale files. Descriptions, and names for historical (inactive) tests in Danish, Norwegian, Finnish and Icelandic, are machine-translated from Swedish/English and should be reviewed before you fully rely on them.

Fetch the whole list:

GET
https://api.webperf.se/v1/tests

Or a single test type by its id number:

GET
https://api.webperf.se/v1/tests/number

For example the following for the performance test (Sitespeed.io):

GET
https://api.webperf.se/v1/tests/15

There are two optional query parameters:

Example with English names and active tests only:

GET
https://api.webperf.se/v1/tests?lang=en&active=true

The response contains the following data:

The response is delivered with long cache headers and an ETag, so a conditional request with If-None-Match can return status code 304 (Not Modified) and save bandwidth.

1.4 More open endpoints

Beyond those above there are some newer open endpoints. They return the same municipalities and regions as 1.1, with the same id numbers, but in a more modern format with the ratings included, pagination and search. All respond with JSON and need no key.

ratings has overall, accessibility, performance, standards and security as decimals between 1 and 5. A rating is null when the website has no rating for that area.

Every open endpoint uses the names id, title, website and same_as. Some of them also keep an older name for the same value, so that existing integrations keep working. Prefer the common names in new code.

Common nameOlder nameFound in
titlename/v1/public/
websiteurl/public-sector/ and /item/
same_assameAs/public-sector/ and /item/

Only open websites appear in the open endpoints. With Premium you reach your own websites with your API key even when they are not open, see 2.7.

2. Webperf's premium API

Quick links to the premium endpoints:

Only for you with Webperf Cloud on the Standard or Agency plan. You need to fetch your API key via your account to get started. If you don't have your API key yet, log in to premium.webperf.se and click API key in the menu to create one.
Read more about Webperf's different plans »

Good to know about numbers. Every endpoint delivers numbers as numbers. Counts and id numbers are integers, for example "result_count": 17, and scores are decimals, for example "rating": 4.92. A missing score is null. Up to version 1.0.5 the older endpoints (the ones under /0.1/ and without a prefix) delivered numbers as strings and missing scores as "-1", see the version history.

Rate limits. Limits are counted per endpoint and per IP address, for the open endpoints as well as for premium. The older endpoints (without a prefix and under /0.1/) allow 100 calls per hour unless stated otherwise, and some have their own, lower limits. The open /v1/ endpoints allow between 60 and 120 calls per minute. Every response carries the headers X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and Retry-After, also when all is well. X-RateLimit-Limit is the limit that applies to that particular call and Retry-After is the number of seconds until the counter resets. Exceeding the limit gives status code 429.

The premium APIs are versioned so that you can trust that your integrations won't suddenly break. Each version is supported for at least 6 months, and it may become longer as the API is released in newer versions going forward.
You don't have to pick a version in your calls, but if you do, it's an extra path segment with a decimal number in the API endpoints below. If you don't pick a versioned endpoint you always get the latest version.

If you don't have premium but want to try it out, you can use the demo account that exists for Webperf.se as a site. In that case it's API key 989089bfe899d6992ff320a97 and the site's id number 3843 that you use.

Looking for code examples to work with the premium API?

There are code examples in Python at the link below. It runs in Jupyter Notebook, which is also compatible with Google Colab as an environment if you prefer that.
With the code example you can interact with the premium API by asking which id number your website has, fetch the latest results, historical average scores and create a graph of the average scores over time.

These are just code examples. To give inspiration for what you can do to get started with the premium API. There are surely other automations and integrations you'll think of.

2.1 Fetch your latest scores

Here you get, in JSON format, the same tests you can see on Webperf.se as test results.

Initially you may not know which id number your website has. Then you can make the API request below together with your API key, and you'll find out which id numbers you have access to. This is also shown on the Info page if you log in to premium.

GET
https://api.webperf.se/stats/
https://api.webperf.se/0.1/stats/

HTTP header
Key: api-key
Value: your-api-key

The response has a list sites where each website has site-ID (the id number, an integer), title and website (name and address), uri (your URI as in 2.7) plus endpoint-current-stats and endpoint-historic-stats with ready-made addresses for 2.1 and 2.2. If the key is missing or invalid, this particular listing answers with status code 400; the per-website endpoints answer 401.

If you only want to check that the key works, for instance in a CI job, there is a cheaper call that lists no websites and never includes the key itself in the response:

GET
https://api.webperf.se/whoami/
https://api.webperf.se/0.1/whoami/

HTTP header
Key: api-key
Value: your-api-key

The response contains user_id, display_name, role (user, agency_admin or super_admin), site_count (the number of websites the key reaches) and unrestricted (true for super admins). A missing or invalid key gives 401.

Once you know the website's id number, your API endpoint is the following to get statistics:

GET
https://api.webperf.se/stats/number
https://api.webperf.se/0.1/stats/number

HTTP header
Key: api-key
Value: your-api-key

There are two optional query parameters:

It might look like this if you use Webperf.se's demo account:

GET
https://api.webperf.se/stats/3843

HTTP header
Key: api-key
Value: 989089bfe899d6992ff320a97

The response contains the following data:

2.2 Historical scores

Webperf has collected test results for many years for certain websites. For example monthly for all municipalities since the summer of 2018. All scores are part of a kind of "category score", which is what you see reported in the recurring monthly reports on Webperf.se, but it can be tedious to compile your own data from those reports. That's why this API endpoint exists.

GET
https://api.webperf.se/stats_per_month/number
https://api.webperf.se/0.1/stats_per_month/number

HTTP header
Key: api-key
Value: your-api-key

It might look like this if you use Webperf.se's demo account:

GET
https://api.webperf.se/stats_per_month/3843

HTTP header
Key: api-key
Value: 989089bfe899d6992ff320a97

The response contains the following data:

2.3 Re-test the start page

Do you want to manually re-run the tests of your website so that Webperf.se takes a fresh look? The test applies to the start page of your website and the result will be published on Webperf.se when the test is done, and you can reach the new tests via the API through 2.1 Fetch your latest scores.

You have a limited number of credits in a rolling 30-day period. Each test costs 1 credit. If the whole order doesn't fit in what you have left it is refused with status code 400 – 2.12 Remaining credits in your quota tells you what is left before you order.

The number you supply is the website's id number. If you don't know your website's id number, run the API request under 2.1 Fetch your latest scores and you'll get it in the response.

POST
https://api.webperf.se/retest/
https://api.webperf.se/0.1/retest/

HTTP header
Key: api-key
Value: your-api-key

Key: site-id
Value: number

Optional – if you only want to run certain tests:
Key: testnums
Value: [1, 10, 15]

Without testnums every active test is queued, 1 credit per test. The allowed id numbers are the ones that are active in 1.3 Test types (/v1/tests?active=true). Unknown or inactive ids are silently ignored; if no valid id remains the API answers 400 with No valid test numbers provided.

It might look like this if you use Webperf.se's demo account:

POST
https://api.webperf.se/retest/

HTTP header
Key: api-key
Value: 989089bfe899d6992ff320a97

Key: site-id
Value: 3843

The response to your POST is status code 200 if it went well and {"message": "Website with site-id 3843 will be retested in a couple of minutes"}. Error cases: 400 if site-id or api-key is missing or invalid, 403 if the key does not reach the website, 404 if the website does not exist, 400 with Quota exceeded if the credits do not suffice, and 500 if something went wrong on the server side.

2.4 Start private tests

You can test up to 10 sub-pages of your choice per call to the API. The number of pages you can test is governed by which level of premium your website has. If you have one of the larger premium variants you have room to make several API posts in a row and then wait for the test results to come in over the 15-30 minutes it usually can take.
See 2.5 Fetch a list of your private tests for how, via the API, you get the API calls you make to reach these private tests.

You have a limited number of credits in a rolling 30-day period. Each page tested costs 15 credits – the same price as in the web interface at webperf.cloud. If the whole order doesn't fit in what you have left it is refused with status code 400 – 2.12 Remaining credits in your quota tells you what is left before you order.

POST
https://api.webperf.se/test_once/
https://api.webperf.se/0.1/test_once/

HTTP header
Key: api-key
Value: your-api-key
Key: site-id
Value: number

HTTP body (form-data)
Key: urls
Value: url1
or
Value: url1, url2, url3 – url10

The body is sent as form-data (application/x-www-form-urlencoded or multipart/form-data), not as JSON. Several addresses are separated by a comma followed by a space. Every address must be on the same domain as the website, otherwise the API answers 400.

It might look like this if you use Webperf.se's demo account:

POST
https://api.webperf.se/test_once/
https://api.webperf.se/0.1/test_once/

HTTP header
Key: api-key
Value: 989089bfe899d6992ff320a97
Key: site-id
Value: 3843

HTTP body
Key: urls
Value: https://webperf.se/articles/nyhetsbrev-38/, https://webperf.se/tag/tillganglighet/

The response to your POST is status code 200 if it went well, with the number of tests created and one address per test where the result can later be fetched as in 2.6:

{
    "message": "Created 2 single tests for site-id 3843",
    "endpoints": {
        "7291": "https://api.webperf.se/private_stats/3843/7291",
        "7292": "https://api.webperf.se/private_stats/3843/7292"
    }
}

Status code 400 means something is missing or does not fit: no address given, an address outside the website's domain, or not enough credits (then the message starts with Quota exceeded and says how many are needed and how many remain in the rolling 30-day period).

2.5 Fetch a list of your private tests

These are called private tests because they are not openly accessible on Webperf.se or to other users of the API than those with the same permission as you.

With the API response below you get URIs to fetch all test results for a privately tested URL. This API URI requires you to supply your API key to get the response, see 2.6 Fetch test results for one of your private tests below.

GET
https://api.webperf.se/private_stats/number
https://api.webperf.se/0.1/private_stats/number

HTTP header
Key: api-key
Value: your-api-key

It might look like this if you use Webperf.se's demo account:

GET
https://api.webperf.se/private_stats/3843
https://api.webperf.se/0.1/private_stats/3843

HTTP header
Key: api-key
Value: 989089bfe899d6992ff320a97

The response contains the following data:

2.6 Fetch test results for one of your private tests

Only those with API access to the website at Webperf or logged in to Premium can access these results. They are not shared openly.

GET
https://api.webperf.se/private_stats/number/test_id
https://api.webperf.se/0.1/private_stats/number/test_id

HTTP header
Key: api-key
Value: your-api-key

It might look like this if you use Webperf.se's demo account:

GET
https://api.webperf.se/private_stats/3843/7289
https://api.webperf.se/0.1/private_stats/3843/7289

HTTP header
Key: api-key
Value: 989089bfe899d6992ff320a97

The response has the same frame as 2.1 Fetch your latest scores (api_version, uri, result_count, timestamp and data), but each entry in data has only four fields: test_date, type_of_test, rating and check_report. The per-area reports and json_check_data do not exist for private tests. A test that has not run yet gives an empty data list and result_count 0.

2.7 Your own URI gives you your latest overall scores

This endpoint has some similarities with 1.2 Persistent URI (for municipalities and regions), but with the difference that it applies to your own website. You also get a bit more data because you supply your API key.

Note! Mind the hyphen in api-key in this particular code example. Without a key, a website that is not a municipality or region answers 404.

GET
https://api.webperf.se/item/number

For example the following for Webperf's demo account:

GET
https://api.webperf.se/item/3843

HTTP header
Key: api-key
Value: 989089bfe899d6992ff320a97

The response contains the following data:

So the response is the same as in 1.2 Persistent URI, complemented with the scores node. If you send a key that does not reach the website you get the public variant without scores for municipalities and regions, and 404 for other websites.

2.8 Get a text's readability index (LIX)

A text can be more or less easy to read. LIX is a measure of this and you, with the premium API, can easily check your texts via the API. Sooner or later LIX calculation will be included in the quota system with test credits.

POST
https://api.webperf.se/tools/lix/

HTTP header
Key: api-key
Value: your-api-key
Key: site-id
Value: your-site-id-number

HTTP body (form-data)
Key: content
Value: your text to readability-test, at least 50 words

The endpoint exists only without a version prefix and only takes POST. The text is sent as form-data, not as JSON. The response data sits under message:

It might look like this:

{
    "message": {
        "lix_value": 52,
        "interpretation": "Svår, normalt värde för officiella texter",
        "stats": {
            "words": 312,
            "sentences": 14,
            "long_words": 94,
            "avg_sentence_length": 22.29,
            "long_word_percentage": 30.13
        }
    }
}

Texts under 50 words give status code 400 with {"message": "Text must contain at least 50 words"}. If content is missing altogether it is 400 with No content provided.

2.9 The categories' average scores

The categories of websites that appear on Webperf.se have average scores in each score category. These you can fetch in a structured way with the premium API.

GET
https://api.webperf.se/categories/
https://api.webperf.se/0.1/categories/

HTTP header
Key: api-key
Value: your-api-key

The response contains the following data:

2.10 Trigger a Premium Audit

For you with a Premium account, you can trigger a Premium Audit via the API. The audit always starts from your website's start page and crawls its way to the pages it tests – you can't submit a list of URLs of your own choosing.

pages decides how many pages are crawled and tested, and costs 1 credit per page from your rolling 30-day quota. You can order at most one audit per hour.

POST
https://api.webperf.se/audit/
https://api.webperf.se/0.1/audit/

HTTP headers
Key: api-key
Value: your-api-key
Key: site-id
Value: your-site-id-number
Key: pages
Value: number of pages, for example 50

pages can also be sent as form-data under the same name, if that suits your client better.

The response contains the following data:

{
    "message": "Audit for site-id nnnn will be processed in a couple of hours"
}

Or, if the order doesn't fit in your remaining credits, status code 400 and:

{
    "message": "Quota exceeded. This audit needs 200 credits but only
    40 remain for the current 30-day period."
}

2.11 Fetch your Premium Audits or reviews

If you have already run a Premium Audit or want to know whether the one you ordered is done yet, you can use this endpoint. Under data you may get an empty result if nothing is finished, a single result as in the example below, or more than one result.

Variants of format are csv for primarily automated accessibility tests, audit and audit_v2.

This is how the results work:

POST
https://api.webperf.se/audits/
https://api.webperf.se/0.1/audits/

HTTP headers
Key: api-key
Value: your-api-key
Key: site-id
Value: your-site-id-number

The response looks as follows (if you have access):

{
    "api_version": "0.1",
    "data": [
        {
            "content": "{\"date\": \"YYYY-MM-DD\", \"folder\": \"/static/results/webbplats.se_yyyymmdd_hhmmss-summary_reports/\", \"base_url\": \"https://webbplats.se\"}",
            "date_modified": "2025-05-12T22:20:01+02:00",
            "format": "audit",
            "result_files": [
                "https://api.webperf.se/0.1/audit_file/?folder=webbplats.se-2025_05_12_18-results&file=accessibility_summary.json",
                "https://api.webperf.se/0.1/audit_file/?folder=webbplats.se-2025_05_12_18-results&file=performance_summary.json",
                "https://api.webperf.se/0.1/audit_file/?folder=webbplats.se-2025_05_12_18-results&file=seo_summary.json",
                "https://api.webperf.se/0.1/audit_file/?folder=webbplats.se-2025_05_12_18-results&file=best-practices_summary.json"
            ],
            "site_id": nnnn,
            "result_id": nnn,
            "title": "Audit Webbplats.se"
        }
    ],
    "result_count": 1,
    "timestamp": "2025-05-28 21:43:02.311244+02:00"
}

site_id is the website's id number, the same one you sent in. result_id is the row's own id. content is a string holding JSON for newer rows; older rows hold a Python-dict string with single quotes instead, so parse the field tolerantly.

Fetching the report files

The files under result_files are not publicly readable. Each file is fetched with the same api-key and site-id as the listing, and the folder must belong to your website – a valid API key does not give you access to anyone else's reports. Call the URL as-is with your headers:

GET or POST
https://api.webperf.se/0.1/audit_file/?folder=folder-name&file=file-name

HTTP headers
Key: api-key
Value: your-api-key
Key: site-id
Value: your-site-id-number

For example with curl:

curl -H "api-key: your-api-key" -H "site-id: nnnn" \
  "https://api.webperf.se/0.1/audit_file/?folder=webbplats.se-2025_05_12_18-results&file=seo_summary.json"

Responds 403 if the key lacks access to the website or the folder belongs to someone else, and 404 if the file does not exist. These files used to sit openly on premium.webperf.se/static/results/; those old links stop working.

2.12 Remaining credits in your quota

Orders cost credits: 1 per test in a re-test, 15 per privately tested page and 1 per page in an audit. This is where you see what you have left before you order, rather than finding out from an error message.

The quota is either set on your website directly or derived from its premium level. The period is a rolling 30 days – credits free up continuously, and there is no day of the month when everything resets.

GET
https://api.webperf.se/quota/
https://api.webperf.se/0.1/quota/

HTTP headers
Key: api-key
Value: your-api-key
Key: site-id
Value: your-site-id-number

The response contains the following data:

It might look like this:

{
    "api_version": "0.1",
    "site_id": nnnn,
    "premium_active": true,
    "premium_enddate": "2027-03-31",
    "monthly_quota": 1500,
    "effective_limit": 1500,
    "used": 460,
    "available": 1040,
    "period_days": 30,
    "timestamp": "2026-08-06 09:12:44.102938+02:00"
}

No account type has an unlimited quota. A website with no premium level gets 0 all the way through and cannot order anything.

3. Premium for web agencies

As a web agency there is a special premium access. Part of it is that you can manage both your own website and your clients'. Another is that it gives you access to information that can make your sales work easier. What is exclusive to web agencies' premium is listed below.

3.1 Which web analytics tools are used on websites Webperf follows

This API requires that you have Premium for web agencies.

GET
https://api.webperf.se/webanalytics?api-key=API-key

The response you get from the API is a CSV file, separated by semicolons. For just under 4000 websites important to a digital Nordics, primarily Sweden.

What output do you get? Well:

The response is delivered as text/plain. Without a key, or with a key lacking the permission, the API answers 400 and 403 respectively.

4. Webhook for you with Webperf Premium

Webhooks are a way to have signals sent to your API when a SaaS service has something to tell you. As in this case, that one of Webperf's tests has run and has a test result for you. Read more about webhooks on Webperf.se

To benefit from webhooks via Webperf you first and foremost need to have a premium account. Set up an endpoint so you can receive the post from Webperf. Then you configure each website inside premium.webperf.se so we know where you want your result sent.

Below you have an example of what the content looks like that you get posted to your endpoint. The HTTP call will have a content-type of application/json and the payload below. You have two different signals about which page it concerns in the case of webhooks.

uri is the address you can later look up to check everything about the page as Webperf sees it. But if you test many pages continuously it's worth knowing that for webhooks there is also tested_url which directly tells you which page the test applies to. As usual, some tests don't evaluate that URL but instead go to your 404 page, check email settings, etc. But then you at least know which URL was fed in as the starting point.

        {
            "api_version": "undefined",
            "uri": "https://api.webperf.se/item/3843",
            "tested_url": "https://webperf.se",
            "timestamp": "2024-01-13T22:34:11.519410",
            "result_count": "1",
            "data": {
                "test_date": "2024-01-13T22:34:11.519410",
                "type_of_test": 4,
                "url": "https://webperf.se",
                "rating": 4.95,
                "check_report": "- Webbsidan kan ändå förbättras inom SEO.
                - Tryckmålen har inte lämplig storlek: 99 % av tryckmålen har lämplig storlek ( 4.40 betyg )
                - Alla bildelement har `[alt]`-attribut ( 5.00 betyg )
                - Dokumentet har en metabeskrivning ( 5.00 betyg )
                - Dokumentet har ett `<title>`-element ( 5.00 betyg )
                - Dokumentet har ett giltigt `hreflang`-värde ( 5.00 betyg )
                - Dokumentet har ett giltigt `rel=canonical`-värde ( 5.00 betyg )
                - Dokumentet har läsliga teckenstorlekar ( 5.00 betyg )
                - Dokumentet undviker plugin-program ( 5.00 betyg )
                - Har en `<meta name="viewport">`-tagg med `width` eller `initial-scale` ( 5.00 betyg )
                - Länkar har beskrivande text ( 5.00 betyg )
                - Länkarna är genomsökningsbara ( 5.00 betyg )
                - Sidan har en giltig HTTP-statuskod ( 5.00 betyg )
                - Sidan är inte blockerad från indexering ( 5.00 betyg )
                - robots.txt är giltig ( 5.00 betyg )",
                "check_report_a11y": "",
                "check_report_perf": "",
                "check_report_stand": "",
                "check_report_sec": "",
                "json_check_data": "{}"
            }
        }

5. Version history

A brief look at the versions that have been released and what the plan is for the next version.

6. Tips on tools and resources

To use Webperf's open data you can really just keep using your web browser. At least if you only want to download data. But for the premium API you need to send an API key in your request, which is most easily done with the Postman app if you're not building your call in some development environment.

Resources: