The Lossless Group214 downloadsPull OpenGraph metadata from any URL into note frontmatter — title, image, description, publisher — via OpenGraph.io or Microlink.

Turn a URL in a note's frontmatter into a link card's worth of metadata: title, description, share image, favicon, site name, authors, and publication date. Use it with no API key, or through OpenGraph.io or Microlink.
Requires Obsidian 1.13 or newer. Desktop only.
We manage our site-wide content in Obsidian. Our toolkit section reviews applications and web services, and each review is a note whose frontmatter becomes a card on the site. Metafetch fills in those cards.

It works the same way for notes about papers, articles, and companies. Fetch once, and Bases, Dataview, or your published site can show the source properly.
og:image (including its :url and :secure_url forms), twitter:image, schema.org microdata, <link rel="image_src">, and JSON-LD. It reads tags however the page writes them.Tooling/, Metafetch reads the company's homepage and fills its GitHub org and repo, LinkedIn page, X, YouTube, Discord, and more. It only fills empty fields, and when the page links two equally likely profiles it writes neither.## Fundraising History as a json block, exactly as they arrived, and keeps them out of your frontmatter.from: [direct, firecrawl] falls back to Firecrawl when a site refuses a plain fetch, and Firecrawl Branding adds colours and fonts. One Firecrawl key covers all of it, and any other JSON API is a recipe file, with its own command if you want one.from: [model] and Claude, OpenAI, TrustedRouter, a local LM Studio, or any API you describe in a recipe file fills it from the page's text. Keys stay in Obsidian's keychain.Full notes: changelog/releases/0.2.0.md.
| Command | What it does | Needs |
|---|---|---|
| Direct fetch from script | Fetches the page itself and reads its Open Graph, Twitter, schema.org, and scholarly (citation_*) tags. |
Nothing. No key, no rate limit. |
| Fetch via Microlink | Same fields, through the Microlink API. | Nothing for about 50 requests a day; an optional key raises the limit. |
| Fetch screenshots from website | Takes a screenshot of the site in the note's url through Microlink, saves it to your attachments folder, and embeds it under ## Screenshots with the date it was taken. |
Nothing for about 50 requests a day; an optional Microlink key raises the limit. |
| Fetch via Fireround | Writes the company's funding rounds into the note as a json block under ## Fundraising History, exactly as Fireround returned them. Needs the note's url to be the company's homepage. |
A Firecrawl key. |
| Fetch from a frontmatter URL… | Lists every URL in the note's frontmatter, under any property (url, arxiv, doi…), and fetches the one you pick with the provider you pick. |
Depends on the provider. |
| Fill frontmatter from folder profile | Fills the empty fields the note's folder profile asks for, such as a Tooling note's social links, from the company's own homepage. | Nothing. A profile in zz-cf-lib/frontmatter/. |
| Create example frontmatter profile | Writes a Tooling socials profile into the profiles folder to start from. | Nothing. |
| Create example model recipe | Writes a recipe for Google's Gemini API into zz-cf-lib/recipes/, to show how to add any model API. |
Nothing. |
| Fetch Open Graph data for current file | Opens a modal: overwrite or only fill in missing fields, record errors, update the fetch date. | An OpenGraph.io key. |
| Batch fetch Open Graph data | Scans the current folder for notes with a URL and missing metadata, then processes them with a delay between requests. | An OpenGraph.io key. |
url: "https://arxiv.org/abs/2305.10601"
og_title: "Tree of Thoughts: Deliberate Problem Solving with Large Language Models"
og_description: "Language models are increasingly being deployed for general problem solving…"
og_image: "https://arxiv.org/static/browse/0.3.4/images/arxiv-logo-fb.png"
og_favicon: "https://arxiv.org/static/browse/0.3.4/images/icons/favicon-32x32.png"
og_site_name: "arXiv.org"
og_type: "website"
authors:
- Shunyu Yao
- Dian Yu
- Jeffrey Zhao
- Izhak Shafran
- Thomas L. Griffiths
- Yuan Cao
- Karthik Narasimhan
og_published: "2023-05-17"
og_last_fetch: "2026-10-08T15:04:05.000Z"
Every key name is configurable under Settings → Metafetch → Field names. An optional vault identity code (hex_code: k4m2x9) can be stamped on each fetched note, so you can reference it by something steadier than its filename.
A folder profile says which frontmatter a folder's notes should carry. It's an ordinary note in zz-cf-lib/frontmatter/, so you edit it in Obsidian:
---
title: Tooling socials
applies-to-paths:
- "Tooling/**"
---
```metafetch-profile
fields:
github_profile_url: { platform: github-org, aliases: [github_profle_url] }
github_repo_url: { platform: github-repo }
linkedin_url: { platform: linkedin-company }
x_url: { platform: x }
og_image: { page: image, aliases: [image] }
```
Run Create example frontmatter profile to get the full Tooling version, then Fill frontmatter from folder profile on a Tooling note. How a social link is chosen:
sameAs) win.A field that already has a value, under its own name or an alias, is never changed.
Some fields need judgment rather than a link: a one-line zinger, a pricing model, a category. Mark them from: [model] with an instruction:
zinger:
from: [model]
describe: "One punchy sentence, under 90 characters, saying what this does and for whom."
pricing_model:
from: [model]
type: enum
values: [free, freemium, paid, open-source, enterprise]
describe: "How the product charges."
After the free fields are filled, the ones still empty go to a model in one call per note, along with the page's text. The reply is checked against each field: a value outside an enum or list is dropped, and the model is told to return nothing rather than guess. Social links and other URLs are never asked of a model.
Some facts aren't on the page at all: who led the last round, how much it raised. Others are, but the site refuses a plain fetch. Name a data recipe in a field's from: list and the output to read in value::
og_image:
page: image
from: [direct, firecrawl] # the page first; Firecrawl if the site refuses
latest_round:
from: [fireround]
value: round # "Series B"
latest_round_amount:
from: [fireround]
value: amount # "$75M"
lead_investors:
from: [fireround]
value: lead_investors # a list
brand_color:
from: [firecrawl-branding]
value: primary_color
Sources run in order: the free parse, then data recipes, then a model, which only fills what nothing else could. Each recipe is called at most once per note, and only while a field still needs it. The recipe's output names stay in the recipe; your vault's key names stay in the profile, so either can change without the other.
| Recipe | What it reads | Outputs |
|---|---|---|
firecrawl |
The page, through Firecrawl's scraper | title, description, image, favicon, site_name, type, published, language, keywords |
firecrawl-branding |
The site's brand, through Firecrawl | logo, favicon, image, primary_color, secondary_color, accent_color, background_color, color_scheme, heading_font, body_font, fonts |
fireround |
The company's latest round, from Fireround's verified feed | round, round_stage, amount, amount_usd_millions, announced_on, lead_investors, other_investors, total_raised, valuation, previous_rounds, sources, fireround_url, company_name, company_description, sector, headquarters |
Fireround is matched on the note's domain, so a company with a similar name is never written. A field the source has no value for stays empty.
Settings → Metafetch → Providers lists one group per key. Model providers each have a key and a model:
| Provider | Default model | Notes |
|---|---|---|
| Claude (Anthropic) | claude-opus-5-5 |
Reply held to a JSON schema |
| OpenAI | gpt-6-astra |
Reply held to a strict JSON schema |
| TrustedRouter | openai/gpt-6-astra |
One key for hundreds of models, through an attested gateway |
| OpenAI-compatible | you choose | LM Studio, Ollama, OpenRouter, Groq: set the base URL |
| Your own | you choose | Any JSON-over-HTTP model API, as a recipe file in zz-cf-lib/recipes/ |
The Firecrawl group holds one key for firecrawl, firecrawl-branding, and fireround, and that key only ever goes to api.firecrawl.dev.
API keys are picked from Obsidian's keychain, never typed into Metafetch's settings file. The keychain belongs to the vault, so one secret named anthropic works for every plugin that uses it. Each key goes only to its provider's own host; a recipe file's key goes nowhere until you approve its host.
To add any other model API, run Create example model recipe and edit the copy. A recipe says where to send the request, where the key goes, what the body looks like (with {{model}}, {{system}}, {{prompt}}, and {{schema}} filled in), and where the reply's text is. Recipes are data only, never code.
A data recipe is the same, with kind: data and named outputs instead of reply text. Its body can use {{url}}, {{domain}} (the note's URL without www.), and {{title}}. With provider: firecrawl it borrows the Firecrawl key, which can still only go to Firecrawl's host. That makes anything in Firecrawl's provider catalogue one file away:
```cf-recipe
id: my-catalogue-lookup
kind: data
provider: firecrawl
title: My catalogue lookup
request:
url: https://api.firecrawl.dev/v2/scrape
body:
alexandria: { provider: <provider>, capability: <capability>, options: { domain: "{{domain}}" } }
response:
error: data.alexandria[0].error # a provider error inside a 200
records: data.alexandria[0].data.data # a list of results…
match: { domain: "{{domain}}" } # …of which only an exact match is read
outputs:
founded: founded_year
founders: founders[*].name # [*] reads every element: a list
website: [website, homepage] # the first path that has a value
```
Add write-json-under: "Some Heading" under response: and the recipe gets its own Fetch via command, like Fetch via Fireround. It writes the raw records into the note body as a json block under that heading, replacing any block already there, so a large answer stays out of your frontmatter. json-key: names the key they sit under, and homepage-only: true at the top level of the recipe skips notes whose url is a deeper page. The command appears after a rescan or the next time a recipe is loaded.
A data recipe with no provider: holds its own key, and that key goes nowhere until you approve its host in settings. Values are written only as text, numbers, yes/no, or lists of those; objects and inline data: images are dropped.
url: property to a note.Cmd+P / Ctrl+P) and run Metafetch: Direct fetch from script.For the OpenGraph.io commands, add your key under Settings → Metafetch → OpenGraph.io. Free keys are available at opengraph.io.
Metafetch also works with Microlink.
Metafetch builds with pnpm and esbuild, and has no runtime dependencies.
git clone https://github.com/lossless-group/metafetch.git
cd metafetch
pnpm install
pnpm dev # watch build to main.js
pnpm test # node:test suites, no framework
pnpm lint # the Obsidian review bot's rules (eslint-plugin-obsidianmd)
pnpm build # type-check + production bundle
pnpm-workspace.yaml is this plugin's own pnpm settings file, so it installs and builds on its own even inside a larger checkout.
To try a build in your vault, symlink the repo into the vault's plugin folder, then fully quit and reopen Obsidian (Obsidian reads main.js only at startup):
ln -s "$(pwd)" "<your-vault>/.obsidian/plugins/metafetch"
Releases are built and published by .github/workflows/release.yml when a version tag is pushed. The assets carry build-provenance attestations:
gh attestation verify main.js --repo lossless-group/metafetch
The Unlicense. Made by The Lossless Group.