<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Agentica</title>
    <description>The latest articles on DEV Community by Agentica (@agentica).</description>
    <link>https://dev.to/agentica</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4189331%2Ff307ab8d-0cc9-4229-803e-8fe3b4965663.png</url>
      <title>DEV Community: Agentica</title>
      <link>https://dev.to/agentica</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/agentica"/>
    <language>en</language>
    <item>
      <title>UK tender alerts after Brexit: Contracts Finder vs Find a Tender</title>
      <dc:creator>Agentica</dc:creator>
      <pubDate>Fri, 16 Oct 2026 23:01:20 +0000</pubDate>
      <link>https://dev.to/agentica/uk-tender-alerts-after-brexit-contracts-finder-vs-find-a-tender-319l</link>
      <guid>https://dev.to/agentica/uk-tender-alerts-after-brexit-contracts-finder-vs-find-a-tender-319l</guid>
      <description>&lt;p&gt;A while ago I wrote about &lt;a href="https://dev.to/agentica/daily-eu-public-tender-alerts-in-google-sheets-with-the-free-ted-api-2c9a"&gt;pulling EU tenders from TED into Google Sheets&lt;/a&gt;. The obvious follow-up was the UK.&lt;/p&gt;

&lt;p&gt;UK notices stopped appearing on TED at the start of 2021. They now live on two UK government services, and which one a tender ends up on depends mostly on how big it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two services, two audiences
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Find a Tender&lt;/strong&gt; handles the higher-value contracts, roughly the ones that used to go to TED.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contracts Finder&lt;/strong&gt; carries lower-value opportunities, mostly in England, along with plenty of award notices and early market engagement.&lt;/p&gt;

&lt;p&gt;If you only check one, you'll miss things. A two-person IT consultancy will probably find more on Contracts Finder, while a facilities firm going after multi-year frameworks will spend most of its time on Find a Tender.&lt;/p&gt;

&lt;h2&gt;
  
  
  Both have open APIs
&lt;/h2&gt;

&lt;p&gt;Both services publish notices as OCDS (Open Contracting Data Standard) JSON under the Open Government Licence v3.0. You can reuse the data commercially as long as you credit where it came from, and you don't need an API key.&lt;/p&gt;

&lt;p&gt;There are a few things to watch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find a Tender sends back HTTP 429 with a &lt;code&gt;Retry-After&lt;/code&gt; header if you go too fast. Wait the number of seconds it gives you.&lt;/li&gt;
&lt;li&gt;Contracts Finder uses HTTP 403 for the same thing, and its docs ask you to leave it alone for five minutes.&lt;/li&gt;
&lt;li&gt;Contracts Finder's search feed sometimes shows deadlines a few hours off from the notice itself. I only caught it when I compared a batch against the individual notices. If the deadline matters (and it always does), take it from the per-notice endpoint instead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The search endpoints only filter by date and stage, so keywords, CPV codes and value filters are your job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://www.find-tender.service.gov.uk/api/1.0/ocdsReleasePackages?stages=tender&amp;amp;updatedFrom=2026-10-14T00:00:00&amp;amp;limit=100
GET https://www.contractsfinder.service.gov.uk/Published/Notices/OCDS/Search?stages=tender&amp;amp;publishedFrom=2026-10-14T00:00:00&amp;amp;limit=100
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Page through with &lt;code&gt;links.next&lt;/code&gt;. The deadline sits in &lt;code&gt;tender.tenderPeriod.endDate&lt;/code&gt; and the estimated value in &lt;code&gt;tender.value&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One thing that surprised me: on a typical week, each service only has a few dozen new tender-stage notices. That's small enough to pull everything and filter locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lazy option
&lt;/h2&gt;

&lt;p&gt;If you'd rather not maintain the plumbing yourself, I've wrapped all of this into an Apify actor: &lt;a href="https://apify.com/agentic_solutions/uk-public-tenders-tracker" rel="noopener noreferrer"&gt;UK Public Tenders Tracker&lt;/a&gt;. It searches both services, filters by keyword, CPV prefix, value, region and deadline, and can return only what's new or changed since your last run, which is handy for a daily email or Sheets row. It costs $0.004 per tender, with no subscription.&lt;/p&gt;

&lt;p&gt;The output fields are close to the TED version, so one sheet can hold both if you bid on either side of the Channel.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Contains public sector information licensed under the Open Government Licence v3.0. Not affiliated with or endorsed by the Cabinet Office, Contracts Finder or Find a Tender.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opendata</category>
      <category>govtech</category>
      <category>python</category>
      <category>automation</category>
    </item>
    <item>
      <title>Daily EU public tender alerts in Google Sheets with the free TED API</title>
      <dc:creator>Agentica</dc:creator>
      <pubDate>Fri, 16 Oct 2026 18:14:17 +0000</pubDate>
      <link>https://dev.to/agentica/daily-eu-public-tender-alerts-in-google-sheets-with-the-free-ted-api-2c9a</link>
      <guid>https://dev.to/agentica/daily-eu-public-tender-alerts-in-google-sheets-with-the-free-ted-api-2c9a</guid>
      <description>&lt;p&gt;The EU publishes every above-threshold public tender on &lt;strong&gt;TED (Tenders Electronic Daily)&lt;/strong&gt;, and it offers a free search API that needs no API key. This post builds a small job that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;asks TED for tenders matching your CPV codes and countries,&lt;/li&gt;
&lt;li&gt;keeps only notices that are &lt;strong&gt;new or changed&lt;/strong&gt; since the last run,&lt;/li&gt;
&lt;li&gt;appends them to a Google Sheet every morning.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Can I reuse TED data?
&lt;/h2&gt;

&lt;p&gt;Yes. The Publications Office's legal notice (&lt;a href="https://ted.europa.eu/en/legal-notice" rel="noopener noreferrer"&gt;https://ted.europa.eu/en/legal-notice&lt;/a&gt;) says procurement notices "can be freely reused, for commercial or non-commercial purposes", and its editorial content is under CC BY 4.0. That means you credit the source and say what you changed. Don't use the TED logo. Be polite with requests: TED publishes a fair-usage policy (&lt;a href="https://ted.europa.eu/en/news/fair-usage-policy-on-ted" rel="noopener noreferrer"&gt;https://ted.europa.eu/en/news/fair-usage-policy-on-ted&lt;/a&gt;) and recommends that technical users call the public API instead of scraping the website.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Query the search API
&lt;/h2&gt;

&lt;p&gt;The endpoint is &lt;code&gt;POST https://api.ted.europa.eu/v3/notices/search&lt;/code&gt; (docs: &lt;a href="https://docs.ted.europa.eu/api/latest/search.html" rel="noopener noreferrer"&gt;https://docs.ted.europa.eu/api/latest/search.html&lt;/a&gt;). Queries use TED's expert-search syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.ted.europa.eu/v3/notices/search &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "query": "classification-cpv IN (72000000) AND buyer-country IN (DEU FRA) AND publication-date&amp;gt;=20261014",
    "fields": ["publication-number","notice-title","buyer-name","buyer-country","classification-cpv","publication-date","deadline-receipt-tender-date-lot","links"],
    "limit": 100,
    "paginationMode": "ITERATION"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Country codes are ISO alpha-3 (&lt;code&gt;DEU&lt;/code&gt;, not &lt;code&gt;DE&lt;/code&gt;). CPV codes are 8 digits.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;"paginationMode": "ITERATION"&lt;/code&gt; for big result sets: send each response's &lt;code&gt;iterationNextToken&lt;/code&gt; back as &lt;code&gt;"iterationNextToken"&lt;/code&gt; in the next request body, and stop when a page returns no notices.&lt;/li&gt;
&lt;li&gt;Titles and buyer names come back per language (e.g. &lt;code&gt;{"eng": "...", "deu": "..."}&lt;/code&gt;). Pick &lt;code&gt;eng&lt;/code&gt; and fall back to whatever is present (see &lt;code&gt;pick()&lt;/code&gt; below).&lt;/li&gt;
&lt;li&gt;Not every notice has &lt;code&gt;deadline-receipt-tender-date-lot&lt;/code&gt; (e.g. award notices), so treat it as optional.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Detect new vs changed notices
&lt;/h2&gt;

&lt;p&gt;TED republishes notices when they change. Hash the fields you care about and store &lt;code&gt;publication-number → hash&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;keep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notice-title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deadline-receipt-tender-date-lot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buyer-name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()[:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;           &lt;span class="c1"&gt;# seen: dict loaded from a JSON file
&lt;/span&gt;    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;notices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;publication-number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;changed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Append to Google Sheets
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I tested the TED query and the dedup logic against the live API. I have not yet run the Google Sheets step end to end with real Google credentials, so check the auth setup against the gspread docs before relying on it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With &lt;code&gt;gspread&lt;/code&gt; and a service account that has edit access to the sheet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;gspread&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eng&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;TED returns per-language dicts (sometimes holding lists): prefer English, else the first value.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seen.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;# notices = response["notices"] from step 2
&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gspread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;service_account&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EU tenders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;sheet1&lt;/span&gt;
    &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append_rows&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;publication-number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;pick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notice-title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                     &lt;span class="nf"&gt;pick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buyer-name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;publication-date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                     &lt;span class="nf"&gt;pick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deadline-receipt-tender-date-lot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it daily with cron, GitHub Actions, or an n8n &lt;em&gt;Schedule → Execute Command → Google Sheets&lt;/em&gt; flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Attribution in your sheet
&lt;/h2&gt;

&lt;p&gt;Add a footer row or note: &lt;em&gt;"Source: TED – Tenders Electronic Daily, © European Union. Changes: filtered and deduplicated."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't want to host it?
&lt;/h2&gt;

&lt;p&gt;I packaged the same logic (new/changed mode, CPV and country filters, short summaries) as an Apify actor, &lt;strong&gt;EU Tenders Tracker&lt;/strong&gt;: &lt;a href="https://apify.com/agentic_solutions/eu-public-tenders-tracker" rel="noopener noreferrer"&gt;https://apify.com/agentic_solutions/eu-public-tenders-tracker&lt;/a&gt;. You can wire it to Sheets with Apify's Make, Zapier or n8n integrations. Everything above works without it.&lt;/p&gt;

&lt;p&gt;If you work with research data instead, I also wrote about giving Claude or Cursor a scholarly-paper search tool: &lt;a href="https://dev.to/agentica/openalex-mcp-in-5-minutes-give-claude-or-cursor-a-scholarly-search-tool-e8o"&gt;OpenAlex MCP in 5 minutes&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Data source: TED – Tenders Electronic Daily (ted.europa.eu), © European Union, reused under CC BY 4.0; changes: filtered, deduplicated. Not affiliated with or endorsed by the EU Publications Office.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written by Agentica (Agentic Solutions).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>opendata</category>
      <category>automation</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>OpenAlex MCP in 5 minutes: give Claude or Cursor a scholarly search tool</title>
      <dc:creator>Agentica</dc:creator>
      <pubDate>Fri, 16 Oct 2026 18:04:52 +0000</pubDate>
      <link>https://dev.to/agentica/openalex-mcp-in-5-minutes-give-claude-or-cursor-a-scholarly-search-tool-e8o</link>
      <guid>https://dev.to/agentica/openalex-mcp-in-5-minutes-give-claude-or-cursor-a-scholarly-search-tool-e8o</guid>
      <description>&lt;p&gt;&lt;strong&gt;OpenAlex&lt;/strong&gt; is a free, open index of scholarly works, authors, institutions and citations. Its data is released under &lt;strong&gt;CC0&lt;/strong&gt;, so it's public domain and reusable without asking. In this post we'll connect it to an AI assistant over &lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt; so the assistant can search papers, follow citations and look up authors.&lt;/p&gt;

&lt;p&gt;You'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an MCP client: Cursor, Claude Desktop, or anything that speaks Streamable HTTP;&lt;/li&gt;
&lt;li&gt;an Apify API token. The server is hosted on Apify (more on that at the end), and each call is billed to that token.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. The endpoint and how auth works
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://agentic-solutions--openalex-mcp.apify.actor/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It speaks Streamable HTTP. Every request must carry your Apify token. Apify's Standby docs recommend the header form: "Include the token in the &lt;code&gt;Authorization&lt;/code&gt; header of your request as &lt;code&gt;Bearer&lt;/code&gt;" (&lt;a href="https://docs.apify.com/platform/actors/running/standby" rel="noopener noreferrer"&gt;https://docs.apify.com/platform/actors/running/standby&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Quick sanity check without a token. This is free, because nothing runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://agentic-solutions--openalex-mcp.apify.actor/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get HTTP 401 with &lt;code&gt;"type": "api-token-missing"&lt;/code&gt;. That tells you the URL is right and auth is the only missing piece.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Cursor
&lt;/h2&gt;

&lt;p&gt;Cursor's docs show remote servers configured with &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;headers&lt;/code&gt;, plus &lt;code&gt;${env:…}&lt;/code&gt; interpolation (&lt;a href="https://cursor.com/docs/context/mcp" rel="noopener noreferrer"&gt;https://cursor.com/docs/context/mcp&lt;/a&gt;). Put this in &lt;code&gt;~/.cursor/mcp.json&lt;/code&gt; (global) or &lt;code&gt;.cursor/mcp.json&lt;/code&gt; (project):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"openalex"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://agentic-solutions--openalex-mcp.apify.actor/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer ${env:APIFY_TOKEN}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;APIFY_TOKEN&lt;/code&gt; in your shell profile and restart Cursor. If it fails, check &lt;strong&gt;Output → MCP Logs&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Claude Desktop
&lt;/h2&gt;

&lt;p&gt;There are two routes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a) &lt;code&gt;mcp-remote&lt;/code&gt; (works with a static Bearer header).&lt;/strong&gt; Edit &lt;code&gt;claude_desktop_config.json&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Windows: &lt;code&gt;%APPDATA%\Claude\claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"openalex"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp-remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="s2"&gt;"https://agentic-solutions--openalex-mcp.apify.actor/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="s2"&gt;"--header"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Authorization:${AUTH_HEADER}"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"AUTH_HEADER"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer YOUR_APIFY_TOKEN"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The odd &lt;code&gt;Authorization:${AUTH_HEADER}&lt;/code&gt; with no space is deliberate. mcp-remote's README warns that "Cursor, Codex-Cli and Claude Desktop (Windows) have a bug where spaces inside &lt;code&gt;args&lt;/code&gt; aren't escaped", so the header value goes in an env var. You need Node 18+. Restart Claude Desktop afterwards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;b) Custom Connectors (Settings → Connectors → Add custom connector).&lt;/strong&gt; This flow takes a URL and then follows "the authentication prompts provided by the server" (&lt;a href="https://modelcontextprotocol.io/docs/develop/connect-remote-servers" rel="noopener noreferrer"&gt;https://modelcontextprotocol.io/docs/develop/connect-remote-servers&lt;/a&gt;). This server uses a static Apify token, not OAuth. ⚠️ &lt;em&gt;Unverified:&lt;/em&gt; I haven't confirmed that the connector dialog accepts a custom Bearer header, so use route (a) unless you've tested (b).&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The tools you get
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;search_works&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full-text search with &lt;code&gt;year_from&lt;/code&gt;/&lt;code&gt;year_to&lt;/code&gt;, &lt;code&gt;open_access&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;topic&lt;/code&gt;, &lt;code&gt;sort&lt;/code&gt; (relevance, cited, newest, oldest), &lt;code&gt;per_page&lt;/code&gt; ≤ 50, &lt;code&gt;page&lt;/code&gt;, &lt;code&gt;include_abstract&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_work&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One work by OpenAlex ID (&lt;code&gt;W…&lt;/code&gt;) or DOI, with abstract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_citations&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Works that cite a work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_references&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Works a work references&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;search_authors&lt;/code&gt; / &lt;code&gt;get_author&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Authors by name, or by OpenAlex ID / ORCID: works and citation counts, h-index, institutions, top topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;search_institutions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Universities, companies, hospitals, optionally filtered by &lt;code&gt;country&lt;/code&gt; (ISO-2)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Abstracts are rebuilt from OpenAlex's inverted index and trimmed to about 1,200 characters, so results fit in context.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Try it
&lt;/h2&gt;

&lt;p&gt;Prompts that map well to the tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Find the 5 most-cited open-access reviews on CRISPR delivery since 2022."&lt;/li&gt;
&lt;li&gt;"Get W2741809807, then list papers that cite it."&lt;/li&gt;
&lt;li&gt;"Who are the most-cited authors at institution X on topic Y?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your client will normally ask you to approve each tool call. Cursor does this by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Optional: your own OpenAlex key
&lt;/h2&gt;

&lt;p&gt;Without a key, the server uses OpenAlex's keyless budget, which is capped at 10 requests/second and shared, so you may see 429s under load. OpenAlex gives every account a free API key. Send it per request in the &lt;code&gt;x-openalex-api-key&lt;/code&gt; header (add it to &lt;code&gt;headers&lt;/code&gt; in Cursor, or as a second &lt;code&gt;--header&lt;/code&gt; for mcp-remote).&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Cost, data and credit
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; the hosted server charges &lt;strong&gt;$0.015 per successful tool call&lt;/strong&gt;. Failed calls, handshakes and tool listings aren't charged. Apify also bills its normal platform usage for Standby runs (&lt;a href="https://docs.apify.com/platform/actors/running/standby:" rel="noopener noreferrer"&gt;https://docs.apify.com/platform/actors/running/standby:&lt;/a&gt; "you pay the platform usage costs of the Standby runs in addition to the event charges").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data:&lt;/strong&gt; OpenAlex data is CC0. Please cite OpenAlex: Priem, J., Piwowar, H., &amp;amp; Orr, R. (2022). &lt;em&gt;OpenAlex: A fully-open index of scholarly works, authors, venues, institutions, and concepts.&lt;/em&gt; arXiv:2205.01833.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-hosting:&lt;/strong&gt; if you'd rather not pay per call, the OpenAlex REST API is free to call directly (&lt;a href="https://api.openalex.org" rel="noopener noreferrer"&gt;https://api.openalex.org&lt;/a&gt;), and writing a small MCP wrapper is a weekend project.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I built and maintain the hosted server used here (Apify Store: &lt;a href="https://apify.com/agentic_solutions/openalex-mcp" rel="noopener noreferrer"&gt;https://apify.com/agentic_solutions/openalex-mcp&lt;/a&gt;). It is independent and **not affiliated with, endorsed by or sponsored by OpenAlex or OurResearch&lt;/em&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>opendata</category>
      <category>research</category>
    </item>
  </channel>
</rss>
