API
Updated 2025-12-08

New API

The updated API is available through api2.marginalia-search.com.

Pass the API key in the API-Key header on every request. If you want to develop an integration but do not yet have a key, you can use the key public. Note that this key cannot create custom filters, and often hits a rate limit.

Obtaining a key

Free non-commercial

Email contact@marginalia-search.com. The email step is necessary to prevent mass signup abuse.

Provided under CC-BY-NC-SA 4.0.

Paid non-commercial

Buy a non-commercial key to skip the line and support the search engine with a one-time payment. Optional, and will always remain optional.

Provided under CC-BY-NC-SA 4.0.

Commercial

Buy a metered commercial key with no commercial usage restriction or attribution requirement.

Manage your subscription at polar.sh/marginalia-search/portal.

Other terms are negotiable, email contact@marginalia-search.com .

Getting started

A test request using the public key:

$ curl -H"API-Key: ${API_KEY}" -XGET https://api2.marginalia-search.com/search?query=json+api

The following query parameters are understood.

ParamValueDescription
count1-100Number of results
timeout50-250Query execution timeout (ms)
dc1-100Max number of results per domain
pageintSelect results page (1-indexed)
nsfw0No filter
nsfw1(experimental) reduce extreme results
filterstringUse custom filter

Sample code in Python 3

import requests

url = "https://api2.marginalia-search.com/search?query={query}"

rsp = requests.get(url.format(query="linear b"), headers={'API-Key': 'public'})

if rsp.ok:
    data = rsp.json()
    print("Query: ", data['query'])
    print("License: ", data['license'])
    print("")
    for result in data['results']:
        print(result['url'])
        print("\t" + result['title'])
        print("\t" + result['description'])
        print("")
else:
    print("Bad Status " + str(rsp.status_code))

Filters

You can set up custom filters for your API key. This is not allowed on the public key.

MethodEndpointDescription
GET/filterList configured filters
POST/filter/NAMECreate a new filter NAME
GET/filter/NAMERetrieve filter definition for NAME
DELETE/filter/NAMEDelete custom filter NAME

Custom filters are defined in XML. These can be hand rolled, or generated by the filter editor.

XML example

<?xml version="1.0"?>
<filter>
    <domains-include>
        www.google.com
        *.youtube.com
    </domains-include>

    <domains-exclude>
        www.yandex.ru
    </domains-exclude>

    <domains-promote amount="-1.0">
        www.bing.com
        www.yahoo.com
    </domains-promote>

    <!-- Adjust ranking of these terms -->
    <domains-promote amount="1.0">
        www.mojeek.com
    </domains-promote>

    <temporal-bias>OLD</temporal-bias> <!-- or RECENT, or NONE -->

    <terms-require>
        foo
        bar
    </terms-require>

    <terms-exclude>
        baz
    </terms-exclude>

    <!-- Adjust ranking of these terms -->
    <terms-promote amount="5.0">
        quux
    </terms-promote>

    <!-- estimated publication year (janky) -->
    <limit param="year" type="lt" value="1996" />
    <!-- a measure of how much javascript is on the page,
         legacy marginalia search ranking metric -->
    <limit param="quality" type="eq" value="5" />
    <!-- how many documents are on the domain -->
    <limit param="size" type="gt" value="100" />
    <!-- pagerank, 0-255 -->
    <limit param="rank" type="gt" value="20" />
</filter>

Old API (deprecated)

The deprecated API is available through api.marginalia.nu or api.marginalia-search.com. Both endpoints are equivalent and will work as long as the project does, but will not receive new functionality.

Examples:

https://api.marginalia.nu/public/
https://api.marginalia.nu/public/search/json+api
https://api.marginalia.nu/public/search/json+api?count=10

The key public is available for experimentation, as used in the examples above. This key has a shared rate limit across all consumers; when the limit is hit, HTTP 503 is returned.

Query parameters (deprecated)

ParamValueDescription
countintNumber of results
dcintMax number of results per domain
pageintSelect results page (1-indexed)
nsfw0No filter
nsfw1(experimental) reduce extreme results
indexintdeprecated

Sample code in Python 3 (deprecated)

import requests

url = "https://api.marginalia.nu/{key}/search/{query}"

rsp = requests.get(url.format(key='public', query="linear b"))

if rsp.ok:
    data = rsp.json()
    print("Query: ", data['query'])
    print("License: ", data['license'])
    print("")
    for result in data['results']:
        print(result['url'])
        print("\t" + result['title'])
        print("\t" + result['description'])
        print("")
else:
    print("Bad Status " + str(rsp.status_code))

Something missing?

Let me know if there are features you would like added to the API.

See also