# Aapl.se — API-dokumentation

Aapl.se är Sveriges ledande Apple-nyhetssajt sedan 2008. Vi aggregerar ~40 källor
och sammanfattar på svenska med AI. Den här sidan dokumenterar sajtens publika,
skrivskyddade gränssnitt för utvecklare och AI-agenter.

**Viktigt om innehållet:** de svenska sammanfattningarna är Aapl.se:s eget innehåll.
Originalartiklarna tillhör respektive källa — fulltext exponeras aldrig via något
gränssnitt. Citera gärna "enligt {källa}, via Aapl.se" med länk.

Alla svar är UTF-8. Datumfält märkta *ISO* är ISO 8601 (`published_at`).
Om inget annat anges krävs ingen autentisering och endast `GET` stöds.

---

## 1. Anonyma JSON-endpoints (legacy)

Öppna, skrivskyddade och utan autentisering. Endast `apple_relevant`-artiklar
listas. `items`-parametern begränsas till max 100.

### `GET https://aapl.se/entries.json`
De senaste artiklarna (nyast först). Frivillig parameter: `items` (antal, standard 50, max 100).

Svarsform:

```json
{
  "entries": [
    {
      "id": 123,
      "title": "Apple lanserar ny iPhone",
      "url": "https://källa.example/artikel",
      "image": "https://aaplse-vibe-storage.hel1.your-objectstorage.com/<key>",
      "summary": "Svensk AI-sammanfattning av artikeln.",
      "source": "MacRumors",
      "source_id": 7,
      "posted": "about 3 hours",
      "published_at": "2026-07-04T09:21:13Z"
    }
  ]
}
```

`posted` är en människoläsbar relativ tid (svenska ord) och kan vara `null`.
Parsa alltid `published_at` (*ISO*) i stället — det kan vara `null` för äldre poster.

### `GET https://aapl.se/entries/popular.json`
Samma svarsform, men ordnad på popularitet (flest klick först, därefter nyast).
Parameter: `items` (standard 50, max 100).

### `GET https://aapl.se/entries/:id.json`
En enskild artikel. Samma objektform som ett element i `entries` ovan (utan
`entries`-omslaget). `:id` måste vara numeriskt.

### `GET https://aapl.se/feeds.json`
Aktiva nyhetskällor:

```json
{ "feeds": [ { "id": 7, "title": "MacRumors" } ] }
```

### `GET https://aapl.se/feeds/:id.json`
Artiklarna för en källa, i samma form som `/entries.json`. Parameter: `items` (standard 50, max 100).

### `GET https://aapl.se/tags.json`
Alla taggar:

```json
{ "tags": [ { "id": 4, "name": "iPhone" } ] }
```

### `GET https://aapl.se/tags/:id.json`
Artiklarna för en tagg, i samma form som `/entries.json`. Parameter: `items` (standard 50, max 100).

---

## 2. `/api/v1` (modernt API, Bearer-auth)

Bas: `https://aapl.se/api/v1`. De flesta endpoints är publika; skicka en token bara
för personaliserade vyer (följda källor). Autentisering sker med
`Authorization: Bearer <token>`.

Listsvar har ett gemensamt hölje:

```json
{
  "data": [ /* ... */ ],
  "pagination": { "current_page": 1, "per_page": 20, "total": 240,
                  "last_page": 12, "has_next": true, "has_prev": false,
                  "next_page": 2, "prev_page": null },
  "meta": { "api_version": "v1", "timestamp": "2026-07-04T09:21:13Z", "filters": {} }
}
```

Fel returneras som `{ "error": { "type": "...", "message": "..." }, "meta": { ... } }`
med lämplig HTTP-status (`404`, `400`, `401`, `422`).

Paginering: `?per_page=` (max 100). Språkfilter där det anges: `?lang=sv` eller `?lang=en`.

| Metod & sökväg | Auth | Beskrivning |
|---|---|---|
| `POST /api/v1/auth/login` | – | Logga in med `provider` (`email`, `apple`, `google`); returnerar en token. |
| `POST /api/v1/auth/logout` | Bearer | Roterar och ogiltigförklarar den aktuella token. |
| `DELETE /api/v1/auth/account` | Bearer | Raderar det autentiserade kontot permanent. |
| `GET /api/v1/entries` | valfri | Artiklar (nyast först). `?sort=popular`, `?lang=`. |
| `GET /api/v1/entries/popular` | – | Artiklar ordnade på popularitet. |
| `GET /api/v1/entries/follows` | **Bearer** | Artiklar från användarens följda källor. |
| `GET /api/v1/entries/search` | valfri | Sök bland artiklar (semantisk sökning med nyckelordsfallback). `?q=` (krävs), `?limit=` (standard 20, max 50). |
| `GET /api/v1/feeds` | valfri | Aktiva källor (med `entries_count`, `is_following`). |
| `GET /api/v1/feeds/:id` | valfri | En källa i detalj. |
| `GET /api/v1/feeds/:id/entries` | valfri | Artiklar för en källa. |
| `POST /api/v1/feeds/:id/follow` | **Bearer** | Följ en källa (idempotent). |
| `DELETE /api/v1/feeds/:id/follow` | **Bearer** | Sluta följa en källa (idempotent). |
| `GET /api/v1/tags` | – | Taggar med artiklar. |
| `GET /api/v1/tags/:id` | – | En tagg i detalj (`:id` = slug). |
| `GET /api/v1/tags/:id/entries` | – | Artiklar för en tagg. |
| `GET /api/v1/stories` | – | Story-bevakningar, senast uppdaterad först. `?status=active`/`dormant`. |
| `GET /api/v1/stories/:id` | – | En story i detalj med brödtext, medlemsartiklar och föregångare/efterföljare (`:id` = id eller id-slug). |
| `GET /api/v1/podd_episodes` | – | Aapl Pod-avsnitt, nyast först. |
| `GET /api/v1/podd_episodes/:id` | – | Ett avsnitt med kapitel, källor och `transcript_url` (`:id` = episode_id). `?include_transcript=true` bifogar manuset. |

Artikelobjekten i `/api/v1` innehåller `id`, `url`, `main_image_url`,
`published_at` (*ISO*), `source_count` (antal oberoende källor som bevakat samma
händelse), `click_count` (*deprecated* — backas numera av artikelvisningar, inte
av den avvecklade klickräknaren), `popularity_score`, `apple_relevant`, en
`feed`, ett `content`-objekt (`original`/`translated` med `title`, `summary`,
`language`) och `tags`. Även här är `content.*.summary` sammanfattning/utdrag —
aldrig full originaltext.

Söksvar från `/api/v1/entries/search` exponerar samma artikelobjekt som andra
entry-endpoints. Svaret innehåller ingen paginering utan är en rankad topplista med
`meta.mode` som anger om sökningen använde `"semantic"` (vektorsökning) eller
`"keyword"` (automatisk fallback när semantisk sökning inte är tillgänglig eller
ger noll träffar). Parametern `?lang=` stöds inte — den semantiska sökningen är
språkoberoende (svenska söktermer matchar svenska sammanfattningar av engelska
källor). Begränsning: 30 anrop per 10 minuter och IP; 429 vid överskridande.
Tom eller saknad `?q=` returnerar 400.

Story-objekten (AI-komponerade kluster av relaterade artiklar som följer en
händelse över tid) innehåller `id`, `title`, `lead`, `status`
(`active`/`dormant`), `entries_count`, `first_entry_published_at`/
`last_entry_published_at` (*ISO*), `image_url` (ärvd från tidigaste
medlemsartikeln med bild; kan vara `null`) och `url`; detaljvyn lägger till
`body`, `predecessor`/`successors` samt `entries` (medlemsartiklarna som
sammanfattning + metadata — samma innehållssnitt som MCP-verktygen och
markdown-ytorna).
Podd-avsnitten (Aapl Pod, Aapl.se:s korta svenska ljudbriefing) innehåller
`episode_id`, `title`, `description`, `published_on` (*ISO*),
`duration_seconds`, `page_url`, `audio_url`, `cover_url` och
`cover_thumb_url`; detaljvyn lägger till `chapters`, `sources` och
`transcript_url`.

---

## 3. Markdown för agenter

Token-effektiva markdown-ytor med endast metadata + svensk sammanfattning.
Alla bär `X-Robots-Tag: noindex` och sätter aldrig cookies.

- `https://aapl.se/entries/:id.md` — en artikel som markdown (YAML-frontmatter med
  `title`, `source`, `original_url`, `canonical_url`, `published` (*ISO*),
  `tags`, `language`, följt av rubrik + sammanfattning + länk till originalet).
  Lägg `.md` på valfri artikel-URL, eller skicka `Accept: text/markdown`
  (fungerar när headern inte innehåller `*/*`).
- `https://aapl.se/index.md` — de senaste 50 `apple_relevant`-artiklarna som en
  markdown-lista som länkar till respektive `.md`-version.
- `https://aapl.se/{slug}.md` — en produktguide som markdown. Gäller alla
  26 produktguider, familjehubbar,
  generationshubbar och ryktessidor på sajten, t.ex. `mac-mini`, `iphone`,
  `iphone-18` och `apple-watch-12`. Aktuell lista finns alltid på
  `/llms.txt`.
- `https://aapl.se/llms.txt` — startpunkt för agenter (llmstxt.org-format) som knyter
  ihop alla ytor ovan.

---

## 4. MCP-server (Model Context Protocol)

`https://aapl.se/mcp` — stateless Streamable HTTP, ingen autentisering. Anslut som
custom connector i Claude (Settings → Connectors) eller i ChatGPT developer mode.
Servern exponerar skrivskyddade verktyg: `latest_news`, `popular_news`,
`search_news` (semantisk sökning), `get_entry`, `list_feeds`, `list_tags`,
`latest_stories` och `get_story` (story-bevakningar), `list_podd_episodes` och
`get_podd_episode` (Aapl Pod) samt `list_product_guides` och `get_product_guide`
(produktguider och ryktessidor, se avsnitt 3). `list_product_guides` tar en
valfri `page_type` (`product_hub`, `family_hub`, `generation_hub` eller `rumor`); `get_product_guide` kräver en
`slug` (t.ex. `mac-mini`) och returnerar guiden som markdown. Verktygssvaren
följer samma innehållssnitt som markdown-ytorna (sammanfattning + metadata,
aldrig originaltext). Anropen är begränsade till 30 per minut och IP (429 vid
överskridande).

---

## 5. Fel & format

- Okända artiklar/sökvägar returnerar HTTP 404 med en **HTML**-body
  (`public/404.html`), inte JSON — kontrollera statuskoden, inte kroppen.
- `.json`-endpoints i avsnitt 1 kräver `.json`-suffix i sökvägen (inte bara en
  `Accept`-header).
- RSS finns på `https://aapl.se/entries.rss` (senaste 50, med taggar och bilder).

---

*Genererad från Aapl.se. Autentisering med query-parameter (`?token=`) är
avsiktligt odokumenterad och stöds inte för nya integrationer.*
