Nordens historia (poster, in Swedish) · Source check · Identifiers · SPARQL
The linked data behind the poster
A poster is a picture. The facts behind it should be possible to look up, check and reuse. This page explains how the timeline Nordens historia is published as linked data, what we learned on the way, and what comes next. The poster and the other pages are in Swedish; this page is in English.
Background
The poster is a Nordic version of Timeline of World History by Chart Origin, shared on LinkedIn. The idea and the form are theirs. The content is my own selection: 70 periods, 6 cross-border events and 26 milestones in six columns, Iceland, Denmark, Norway, Sweden, Finland and Sápmi.
I made it together with Claude. The first version was written from general knowledge, without sources. Then every statement on the poster was checked against a reference source by AI agents with web search. After that, every item got a persistent identifier and was described as linked data.
What the data showed
The source check gave 166 confirmed and 41 disputed statements, and no statement that was simply wrong. The disputed ones are almost all period boundaries: 34 of the 65 checked boundaries do not match what the source says. The Swedish, Danish and Norwegian reference works do not draw the lines in the same places. The Viking Age ends in 1066 in English convention, but around 1050 in the Nordic reference works. Finnish prehistory has its own chronology, where the Iron Age continues through the Viking and Crusade periods.
This is a typical linked data experience. When the data has to be explicit, the fine details come up early, at the data level, instead of being handled later in software or hidden in footnotes. A period boundary is not a fact. It is a choice made by someone, in a source.
So the model now says exactly that. The poster has its own boundaries, as one choice among others. Each source's boundary is a separate definition: 66 definitions for 66 of the 70 periods. 39 of them agree with the poster and 27 differ. Three boundaries were confirmed by the source and then moved by me afterwards, to make the poster consistent. The data shows this openly:
- Vikingatid (Norge): the source confirmed 793–1066, the poster now has 793–1050.
- Riket konsolideras (Norge): the source confirmed 1066–1130, the poster now has 1050–1130.
- Krigsåren (Finland): the source confirmed 1939–1945, the poster now has 1939–1944.
Identifiers
Every item has an identifier under https://kerfors.github.io/sidospar/nordens-historia/id/.
- Opaque numbers.
nh-pperiod,nh-eevent,nh-mmilestone,nh-cchecked statement. The name is not part of the identifier, so a period can be renamed or re-dated without breaking links. Numbers are never reused. They are stored in the data, not generated at build time. - Thing and page.
…/id/nh-p0030is the period.…/id/nh-p0030/is the page about it, with embedded JSON-LD, andindex.jsonldnext to it. GitHub Pages answers the first with a 301 redirect to the second. Linked data practice says 303. The page states what it is about (schema:mainEntity). - Years. The poster writes "500 f.Kr." (500 BCE). The data uses
xsd:gYearwith astronomical numbering, where 1 BCE is year 0. So 500 BCE is-0499.
The model
The model reuses existing vocabularies. Each one does the job it was made for.
| What | Vocabulary | Why |
|---|---|---|
| Periods and events | CIDOC-CRM: E4_Period, E5_Event, time-span with
P82a/P82b, place with P7_took_place_at |
The cultural heritage standard for things that happen in time and space. |
| The poster's periodisation | SKOS: each period is a concept in the poster's own scheme | A periodisation is a classification of time. The scheme carries the poster's choice and its creator. |
| Sources' boundaries | SKOS in the style of PeriodO: one definition
per source, in a scheme per source, matched to the poster's period with skos:exactMatch or
skos:closeMatch |
PeriodO's insight: a period is a definition made by an authority, not a fact. Conflicting boundaries are data. |
| Checked statements | schema.org: Claim and ClaimReview with verdict,
evidence and source. Each claim has a kind (dcterms:type), a concept in a small SKOS scheme:
a period's name and boundaries, a detail in a period's description, an event, or a milestone |
The vocabulary made for fact checking. Readable by search engines too. |
| Provenance | PROV-O: each review is generated by the source check (an activity by AI agents on my behalf) and derived from its source. Each definition is derived from its review. | The chain from source to data is explicit and can be followed. |
| Relations between items | OWL-Time intervalMeets (71) and CIDOC-CRM
P10_falls_within (39) |
Derived from the poster at build time: which period follows which in the same column, and which events happen during which period. |
| Links out | Wikidata: skos:exactMatch, skos:closeMatch, and
schema:about for milestones |
A milestone is an event, for example the founding of Uppsala University in 1477. Wikidata has the university, not the founding, so the link says "about", not "same as". |
The chain for one period boundary:
As it is in the data (JSON-LD, shortened to these three nodes):
[
{
"@id": "nh:nh-p0030",
"@type": [
"crm:E4_Period",
"skos:Concept"
],
"name": "Vikingatid",
"skos:inScheme": "nh:#perioder",
"crm:P4_has_time-span": {
"@type": "crm:E52_Time-Span",
"crm:P82a_begin_of_the_begin": "0793",
"crm:P82b_end_of_the_end": "1050"
},
"crm:P7_took_place_at": [
"wd:Q34"
],
"skos:exactMatch": "wd:Q10685826",
"time:intervalMeets": [
"nh:nh-p0031"
],
"time:intervalMetBy": [
"nh:nh-p0059"
]
},
{
"@id": "nh:nh-p0030#def-029",
"@type": [
"crm:E4_Period",
"skos:Concept"
],
"dcterms:identifier": "def-029",
"skos:prefLabel": {
"sv": "Vikingatid enligt Statens historiska museer"
},
"skos:inScheme": "nh:#kalla-historiska",
"skos:exactMatch": "nh:nh-p0030",
"crm:P4_has_time-span": {
"@type": "crm:E52_Time-Span",
"crm:P82a_begin_of_the_begin": "0793",
"crm:P82b_end_of_the_end": "1050"
},
"dcterms:spatial": [
"wd:Q34"
],
"dcterms:source": "https://historiska.se/utforska-historien/tidsaldrar/",
"prov:wasDerivedFrom": "nh:nh-c0073#granskning",
"skos:scopeNote": {
"sv": "Källans formulering: vikingatid 793–1050"
}
},
{
"@id": "nh:nh-c0073#granskning",
"@type": [
"ClaimReview",
"prov:Entity"
],
"itemReviewed": "nh:nh-c0073",
"reviewRating": {
"@type": "Rating",
"alternateName": "omdiskuterad"
},
"reviewBody": "Historiska museet anger vikingatid 793–1050; NE anger ca 800–1050. Ingen svensk källa använder 1066 som slut.",
"datePublished": "2026-10-18",
"prov:wasGeneratedBy": "nh:#kontroll-2026-10-18",
"citation": {
"@id": "https://historiska.se/utforska-historien/tidsaldrar/",
"name": "Statens historiska museer – Tidsåldrar"
},
"prov:wasDerivedFrom": "https://historiska.se/utforska-historien/tidsaldrar/",
"schema:correction": "793 – 1050",
"schema:comment": {
"@type": "Comment",
"text": "Åtgärd på affischen: Slut ändrat till 1050."
}
}
]
A second check, against the archives
The first check used reference works: encyclopaedias and museum pages, sources that summarise other sources. For 15 statements about Swedish persons and medieval documents, a second check went one step closer to the sources, to two registers at Riksarkivet, the Swedish National Archives: Svenskt biografiskt lexikon (SBL) and Diplomatarium Suecanum (SDHK), the register of medieval Swedish charters. They were reached through Riksarkivet's MCP service (beta), which lets an AI agent search the registers directly. The check cites 12 SBL articles and 9 SDHK charters.
12 statements were confirmed and 3 could not be checked, because the service gives SBL's summary and bibliography, not the full biography. No statement was contradicted. But the registers add details the reference works leave out:
- The year 1164 for the archbishopric in Uppsala is not in the papal letter. SDHK says the year was supplied by the editors.
- The peace of Nöteborg is dated 12 August 1323 by the editors, and SDHK marks one of the medieval copies as a forgery.
- Erik of Pomerania was crowned in Kalmar on 17 June 1397 according to SBL. SDHK dates his coronation letter 13 July 1397. Same year, different day.
- Stockholm appears in two letters dated at Stockholm in 1252. That they are the first mentions is a conclusion drawn by historians, not something the register says.
It is the same lesson as with the period boundaries, one level down: a date can also be a choice, made by an
editor. In the data, the second check is its own ClaimReview next to the first, generated by its own PROV
activity that used (prov:used) the MCP service, and derived from the register entries it cites.
The first review is unchanged.
Turtle and SPARQL
The same graph is published in two serialisations. JSON-LD is the source: the build writes it, and it is embedded in every page. Turtle is generated from the JSON-LD with rdflib, and the build checks that the two give the same graph. The check is graph isomorphism, not text comparison, because the time-spans and ratings are blank nodes. Every item page shows both formats side by side.
The SPARQL page loads id/index.ttl into
Oxigraph, a SPARQL engine compiled to WebAssembly, in the browser.
There is no server. The page has 9 example queries, for example the disputed boundaries in
Finland, periods without a Wikidata link, and the sources' boundaries for the Viking Age. The build runs every example
query with rdflib and fails if one returns no rows.
How it was made, and the limits
- The source check split the 207 statements by country and gave each group to an AI agent with web search. Each statement needed a source where the agent had actually seen the fact. Nordic reference works came first. The most used sources: snl.no (75), lex.dk (52), ne.se (18), uppslagsverket.fi (18), historiska.se (5), riksdagen.se (3).
- The evidence texts are the agents' own summaries of the source, not quotes. Some Nationalencyklopedin pages are behind a paywall and were checked only against the free introduction.
- Each Wikidata link was found with Wikidata's own search service and then checked against wikidata.org. 72 of 102 items are linked, and all 6 columns point to their place. Most items without a link are my own descriptive period names, and the six Sápmi periods, where no matching Wikidata item was found.
- Boundaries from disputed statements were read from the evidence text, for the cited source only. Approximate years are marked, and the source's own wording is kept.
Data and code
- id/index.jsonld and id/index.ttl: the whole dataset
as JSON-LD and as Turtle. Every item also has its own
index.jsonldandindex.ttl. data/items.json,data/claims.json,data/definitions.json,data/wikidata.json: the source data.data/riksarkivet.json: the second check, against Riksarkivet's registers.check/: the agents' unedited answers from the first check.build.pybuilds everything: the poster, the source check page, the identifier pages, the SPARQL page and this page. The code is in the sidospar repository.- License: CC BY 4.0.
Next steps
- SHACL shapes. The model described as shapes, and the build validated against them.
- More languages. Labels in Danish, Norwegian, Finnish, English and Northern Sami.
- A persistent base. A w3id.org redirect, so the identifiers survive a change of host and can answer with 303.
Kerstin Forsberg, October 2026.