Datacircle

LinkedIn profiles in LangChain4j: a LinkedIn tool through MCP or a Java method

You're building an app in Java with LangChain4j, and the model should read a LinkedIn profile from its URL: the job title, company, location and headline. LangChain4j's docs say "The goal of LangChain4j is to simplify integrating LLMs into Java applications." LangChain4j has no LinkedIn tool of its own.

Datacircle is a data co-op. Step 1: Query your favorite B2B data APIs through us. Same request, same price, no markup. Step 2: You're DONE. Every morning, you get the flat file of your data plus everyone else's. Add $50 to your account: you get $50 of API PLUS the flat file. Right now we have 3 live LinkedIn profile APIs that we trust: Up2Data, HarvestAPI and Fetchin.

You can connect our MCP server to your app and write no tool code. Or you can write one Java method that calls our API, and decide what the model reads. Both call Up2Data first. Up2Data costs $2.375 per 1,000 profiles it finds, and nothing for a profile it can't find. Fetchin is cheaper at $1.485 per 1,000, but it charges for a profile it can't find, and all our customers share its rate limit. Both switch to Fetchin only when Up2Data hits its daily limit and returns a 429.

We ran both apps in plain Java, with a scripted stand-in for OpenAI's API as the model, and a stand-in server that answers like our API. We called api.datacircle.dev with a wrong key, from the method and from the MCP app: each got a 401. We haven't run either with a real key or a real model.

LangChain4j has no LinkedIn tool

In LangChain4j, a tool is a Java method marked @Tool that you give to an AI Service (see LangChain4j's Tools page). LangChain4j's MCP client turns an MCP server's tools into tools the model can call. LangChain4j itself ships no LinkedIn tool.

On October 12, 2026, we searched GitHub's code for "linkedin" in the langchain4j repo and found 0 files. In the langchain4j GitHub organization, we found 11: a sample CV and a life story in one example, a speaker's LinkedIn page in 7 READMEs, and a LinkedIn article's URL in two tests of langchain4j-community. Of the 256 LangChain4j artifacts on Maven Central, including 90 community modules, none has LinkedIn in its name.

Before you start

  • Java 17, or Java 21 or later, and Maven. LangChain4j's Get Started page says "The minimum supported JDK version is 17." We ran both files on Java 17 and Java 25, with Maven 3.9.16. On Java 20, the MCP app crashed at startup, because LangChain4j uses virtual threads, a preview feature in Java 20:
    Exception in thread "main" java.lang.ExceptionInInitializerError
    	…
    Caused by: java.lang.RuntimeException: Failed to create virtual thread executor
    The other way, one Java method, ran on Java 20.
  • A Datacircle API key: Sign up at datacircle.dev with your work email: a $5 credit, that's 2,105 LinkedIn profiles at $2.375 per 1,000. Log in at datacircle.dev/login with your work email. Your API key is on the page once you're in. Put it in DATACIRCLE_API_KEY.
  • An OpenAI API key in OPENAI_API_KEY. Both files pass it to OpenAiChatModel, from the langchain4j-open-ai module, so you don't put the key in any file. LangChain4j has modules for other model providers. We haven't tried them.

Each way is a small Maven project with no framework. This is the MCP way's pom.xml, with LangChain4j's BOM, its AI Services, its OpenAI module and its MCP module:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>agent</artifactId>
  <version>1.0</version>

  <properties>
    <maven.compiler.release>17</maven.compiler.release>
    <!-- keeps the tool method's parameter names, as the model sees them -->
    <maven.compiler.parameters>true</maven.compiler.parameters>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <exec.mainClass>com.example.agent.McpAgent</exec.mainClass>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-bom</artifactId>
        <version>1.22.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>dev.langchain4j</groupId>
      <artifactId>langchain4j</artifactId>
    </dependency>
    <dependency>
      <groupId>dev.langchain4j</groupId>
      <artifactId>langchain4j-open-ai</artifactId>
    </dependency>
    <!-- the MCP way only -->
    <dependency>
      <groupId>dev.langchain4j</groupId>
      <artifactId>langchain4j-mcp</artifactId>
    </dependency>
  </dependencies>
</project>

The MCP way: our server through LangChain4j's MCP client

LangChain4j's MCP client connects to our MCP server, and McpToolProvider hands the server's tools to the model. Our MCP server is at https://api.datacircle.dev/mcp. It gets a LinkedIn profile from its URL, through Up2Data, HarvestAPI or Fetchin. Put this in src/main/java/com/example/agent/McpAgent.java, then run mvn compile exec:java:

package com.example.agent;

import java.util.Map;

import dev.langchain4j.mcp.McpToolProvider;
import dev.langchain4j.mcp.client.DefaultMcpClient;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.transport.http.StreamableHttpMcpTransport;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.tool.ToolArgumentsErrorHandler;
import dev.langchain4j.service.tool.ToolExecutionErrorHandler;

public class McpAgent {

    record Profile(String jobTitle, String company, String location, String headline) {}

    interface ProfileReader {
        @SystemMessage("Read the LinkedIn profile at the user's URL with get_linkedin_profile."
                + " If up2data says its limit is reached, call it again with provider fetchin.")
        Profile read(String url);
    }

    public static void main(String[] args) throws Exception {
        String api = System.getenv().getOrDefault("DATACIRCLE_API_URL", "https://api.datacircle.dev");
        try (McpClient mcp = DefaultMcpClient.builder()
                .transport(StreamableHttpMcpTransport.builder()
                        .url(api + "/mcp")
                        // Your Datacircle key, as a Bearer token on each request to our MCP server
                        .customHeaders(Map.of("Authorization", "Bearer " + System.getenv("DATACIRCLE_API_KEY")))
                        .build())
                .protocolVersion("2025-11-25") // the MCP version our server speaks
                .build()) {
            ProfileReader reader = AiServices.builder(ProfileReader.class)
                    .chatModel(OpenAiChatModel.builder()
                            .baseUrl(System.getenv().getOrDefault("OPENAI_BASE_URL", "https://api.openai.com/v1"))
                            .apiKey(System.getenv("OPENAI_API_KEY"))
                            .modelName("gpt-5.4-mini")
                            .build())
                    // Of our server's tools, keep get_linkedin_profile only
                    .toolProvider(McpToolProvider.builder()
                            .mcpClients(mcp)
                            .filterToolNames("get_linkedin_profile")
                            .build())
                    // Our server's errors reach the model; any other error stops the call
                    .toolExecutionErrorHandler(ToolExecutionErrorHandler.failInvocationUnlessVisibleToLlm())
                    .toolArgumentsErrorHandler(ToolArgumentsErrorHandler.sendExceptionMessageToLlm())
                    // at most 4 answers with tool calls per question
                    .maxToolCallingRoundTrips(4)
                    .build();
            System.out.println(reader.read("https://www.linkedin.com/in/example-profile"));
        }
    }
}

LangChain4j's MCP page shows the transport with a URL only. customHeaders adds your key to each request as a Bearer token. protocolVersion("2025-11-25") skips the first request the client sends by default, which asks which MCP version the server speaks.

ProfileReader is an AI Service: you write the interface, and AiServices builds it. Because read returns a Profile, LangChain4j asks the model for JSON with the record's four fields, and reads its answer into the record.

filterToolNames keeps only get_linkedin_profile of our server's tools. Check the tool name's spelling: when we misspelled the name, the model got no tool, and the app ran with no error. The first request to the model was 2,227 bytes with one tool, and 4,719 with all five tools of our MCP server.

LangChain4j gives the model our JSON, uncut

Our server sends each answer as text and as structuredContent. LangChain4j gives the model the structured content, as compact JSON: the provider's JSON, plus datacircle_meta, what the call cost and your balance after it:

{"data":{…},"meta":{…},"datacircle_meta":{…}}

Up2Data's example answer was 1,167 characters as our text, and 937 as the model read it. Our stand-in server sent a Fetchin answer of 65,229 characters. LangChain4j passed it to the model without the spaces: 63,166 characters, nothing cut. The model gets the tool's name, description and inputs. It doesn't get the tool's output schema, all 8,291 characters of it, or the instructions our server sends when your app connects.

At Up2Data's daily limit, the model reads our error and can call again through Fetchin

The tool takes url, and provider: up2data (the default), harvestapi or fetchin. At Up2Data's limit, the server tells your agent to call again through Fetchin or HarvestAPI. HarvestAPI costs more per 1,000 profiles than the other two, so in the file's system message we tell the model to use Fetchin. Our server sends each error as text only, and the model reads it as we wrote it:

{"error": "daily up2data limit reached for your account …"}

We wrote our scripted model to call again with fetchin on this error, and it got the profile. The model read our text for the other errors we tried, and the app kept running. The file's first error handler passes our server's errors to the model, as LangChain4j's error handling docs say, and stops the app on any other exception. After we removed the two handlers, the model read the same errors, and LangChain4j logged a warning that the file hadn't set them.

With a wrong key, your app stops before any model call

The MCP client connects to our server when you build the client. We tried a wrong key on api.datacircle.dev, and the app stopped there:

Exception in thread "main" java.lang.RuntimeException: java.util.concurrent.ExecutionException: dev.langchain4j.exception.HttpException: Unexpected status code: 401
	at dev.langchain4j.mcp.client.DefaultMcpClient.initializeLegacy(…)
	…
	at com.example.agent.McpAgent.main(…)
…
Caused by: dev.langchain4j.exception.HttpException: Unexpected status code: 401

The trace doesn't show our error message. Set the right key in DATACIRCLE_API_KEY. Without the header, the app sends no key and stops the same way. Without protocolVersion, the error message says to set the protocol version. Ignore that: the wrong key is the cause.

The MCP client waits 60 seconds for a tool call

That's LangChain4j's default. Our API stops waiting for a provider after 45 seconds and returns a 503, so we set no timeout in the file. Only our stand-in server took longer: it answered after 50 seconds, and the model got the profile. In a second test, it answered after 65 seconds. The model read this after 60, and the app kept running:

There was a timeout executing the tool

The function way: one Java method that calls our API

You send the provider's own request to api.datacircle.dev, with your Datacircle key. That's the only change. The method sends Up2Data's and Fetchin's own requests, with X-Data-Provider naming the provider. Up2Data's request is in our API reference. Take the pom.xml above without langchain4j-mcp, and with com.example.agent.ToolAgent in exec.mainClass. Put this in src/main/java/com/example/agent/ToolAgent.java, then run mvn compile exec:java:

package com.example.agent;

import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Map;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.tool.ToolArgumentsErrorHandler;
import dev.langchain4j.service.tool.ToolExecutionErrorHandler;

public class ToolAgent {

    record Profile(String jobTitle, String company, String location, String headline) {}

    interface ProfileReader {
        @SystemMessage("Read the LinkedIn profile at the user's URL with get_linkedin_profile.")
        Profile read(String url);
    }

    public static void main(String[] args) {
        ProfileReader reader = AiServices.builder(ProfileReader.class)
                .chatModel(OpenAiChatModel.builder()
                        .baseUrl(System.getenv().getOrDefault("OPENAI_BASE_URL", "https://api.openai.com/v1"))
                        .apiKey(System.getenv("OPENAI_API_KEY"))
                        .modelName("gpt-5.4-mini")
                        .build())
                .tools(new LinkedInTool())
                // An exception in the method stops the call
                .toolExecutionErrorHandler(ToolExecutionErrorHandler.failInvocationUnlessVisibleToLlm())
                .toolArgumentsErrorHandler(ToolArgumentsErrorHandler.sendExceptionMessageToLlm())
                // at most 4 answers with tool calls per question
                .maxToolCallingRoundTrips(4)
                .build();
        System.out.println(reader.read("https://www.linkedin.com/in/example-profile"));
    }
}

class LinkedInTool {

    static final String API = System.getenv().getOrDefault("DATACIRCLE_API_URL", "https://api.datacircle.dev");
    static final String KEY = System.getenv("DATACIRCLE_API_KEY");
    static final ObjectMapper JSON = new ObjectMapper();
    final HttpClient http = HttpClient.newHttpClient();

    @Tool(name = "get_linkedin_profile", value = """
            Get the current job title, company, location and headline on a LinkedIn profile, \
            from the profile's URL, like https://www.linkedin.com/in/example-profile. \
            Returns jobTitle, company, location and headline, or error when the profile can't be read. \
            Each call asks the provider live and may be billed to the Datacircle balance.""")
    Map<String, String> getLinkedInProfile(String url) throws IOException, InterruptedException {
        String body = JSON.writeValueAsString(Map.of("url", url));
        HttpResponse<String> answer = call("up2data", HttpRequest.newBuilder(URI.create(API + "/v1/profiles/enrich"))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(body)));
        if (answer.statusCode() == 200) {
            JsonNode profile = JSON.readTree(answer.body()).path("data");
            return Map.of("jobTitle", text(profile, "/current_company/title"),
                    "company", text(profile, "/current_company/name"),
                    "location", text(profile, "/location/raw"),
                    "headline", text(profile, "/headline"));
        }
        if (answer.statusCode() == 429) { // Up2Data's daily limit: Fetchin answers instead
            String query = "?profileUrlOrUrn=" + URLEncoder.encode(url, StandardCharsets.UTF_8);
            answer = call("fetchin", HttpRequest.newBuilder(URI.create(API + "/api/v1/profile" + query)).GET());
            if (answer.statusCode() == 200) {
                JsonNode profile = JSON.readTree(answer.body());
                return Map.of("jobTitle", text(profile, "/jobTitle"),
                        "company", text(profile, "/companyName"),
                        "location", text(profile, "/location"),
                        "headline", text(profile, "/title"));
            }
        }
        int status = answer.statusCode();
        return switch (status) {
            case 404, 422 -> Map.of("error", "This LinkedIn profile is private or deleted.");
            case 400 -> Map.of("error", "This is not a LinkedIn profile URL."
                    + " Send one like https://www.linkedin.com/in/example-profile");
            case 402 -> Map.of("error", "The Datacircle balance is too low for this call."
                    + " Tell the user to add funds on their Datacircle dashboard.");
            case 429, 500, 502, 503, 504 -> Map.of("error", "The provider didn't answer (" + status + "),"
                    + " and the call wasn't charged. Try again in a minute.");
            // 401: DATACIRCLE_API_KEY is wrong
            default -> throw new IllegalStateException("Datacircle answered " + status + ": " + answer.body());
        };
    }

    // One call through one provider. Fetchin's 429 is its rate limit: wait 1 s and send it again, then 2 s
    HttpResponse<String> call(String provider, HttpRequest.Builder request) throws IOException, InterruptedException {
        HttpRequest sent = request.header("Authorization", "Token " + KEY)
                .header("X-Data-Provider", provider)
                .timeout(Duration.ofSeconds(60)) // above the 45 seconds our API waits for a provider
                .build();
        HttpResponse<String> answer = http.send(sent, HttpResponse.BodyHandlers.ofString());
        for (int wait = 1; wait <= 2 && provider.equals("fetchin") && answer.statusCode() == 429; wait++) {
            Thread.sleep(wait * 1000L);
            answer = http.send(sent, HttpResponse.BodyHandlers.ofString());
        }
        return answer;
    }

    static String text(JsonNode answer, String pointer) {
        return answer.at(pointer).asText("");
    }
}

@Tool makes the method a tool. The model gets its name, its whole description, and url as a string, but not the method's return type, so the description names the answer's keys. The model sees the parameter's name only if you compile with -parameters, which maven.compiler.parameters sets, as LangChain4j's Tools page says. Without it, the model saw arg0, the method got null, and the app stopped on a NullPointerException. LangChain4j logs a warning about it, but with no SLF4J logger in the project, as in this pom.xml, you won't see it.

The method sends the URL to Up2Data. At Up2Data's daily limit (its 429), it sends the same URL to Fetchin. Fetchin takes 5 requests a second across all our customers, and returns a 429 past that: the method waits one second and sends the request again, then waits two seconds and sends it once more.

The method returns a Map, and LangChain4j gives it to the model as JSON:

{"jobTitle":…,"company":…,"location":…,"headline":…}

For a private or deleted profile, a URL that isn't a profile, a low balance or a provider error, the method returns one key, error, so the model can tell the user. Each field comes from the same JSON path as in our Python post:

Source of each field
FieldUp2Data's answerFetchin's answer
jobTitledata.current_company.titlejobTitle
companydata.current_company.namecompanyName
locationdata.location.rawlocation
headlinedata.headlinetitle

With a wrong key (401), the method throws, and the app stops on the exception before a second model call. We tried a wrong key on api.datacircle.dev:

Exception in thread "main" java.lang.IllegalStateException: Datacircle answered 401: {"error": "invalid api key"}
	at com.example.agent.LinkedInTool.getLinkedInProfile(…)
	…

Set the right key in DATACIRCLE_API_KEY. After we removed the two handlers, LangChain4j gave the model the exception's message instead, and the app kept running.

With no timeout, the JDK's HttpClient waits forever. The method waits 60 seconds: our stand-in server answered after 50 seconds, and the model got the profile. We set a 10 second timeout, and the app stopped 10 seconds into the call. DATACIRCLE_API_URL is for tests: point it at a stand-in for our API, and you can run either file without spending your balance.

Cap the calls with maxToolCallingRoundTrips

Each tool call reaches our API. We bill each call that gets a profile, and each call for a profile Fetchin or HarvestAPI can't find, at the price on our pricing page. The method sends up to four requests per call, and we bill at most one of them: Up2Data's 429 and Fetchin's 429 are free.

LangChain4j counts the model's answers that ask for tools, and stops at 100 by default, as the Javadoc of maxToolCallingRoundTrips says. A scripted model that asked for the tool at every turn made 100 calls to a stand-in for our API in under a second. With maxToolCallingRoundTrips(4), as in both files, it made 4, and the app stopped with this exception:

Exception in thread "main" java.lang.RuntimeException: Something is wrong, exceeded 4 tool calling round trips (maxToolCallingRoundTrips)

One answer can ask for more than one call. Our scripted model asked for two calls in each answer, and made 8 calls under the same cap.

At each turn, LangChain4j sends the model the whole conversation so far. In our MCP test, the model called the tool at every turn, and LangChain4j added the Fetchin answer of 63,166 characters to the conversation each time, so the requests grew from 2,227 bytes to 69,435, 136,643, 203,851 and 271,059.

AI Services run each tool call without asking for your approval. beforeToolExecution on the builder runs your code before each call: when ours threw an exception, the call never reached our server, and the app stopped.

API answers: cost and what the tool returns

Our API's answers to the method in ToolAgent.java
AnswerMeaningCostThe tool returns
Up2Data 200the profile$2.375 per 1,000the four fields
Up2Data 422the profile is private or deletedfreeerror: "This LinkedIn profile is private or deleted."
Up2Data 400not a LinkedIn profile URLfreeerror: "This is not a LinkedIn profile URL."
Up2Data 429its daily limitfreeFetchin's answer
Fetchin 200the profile$1.485 per 1,000the four fields
Fetchin 404 with PROFILE_NOT_FOUNDthe profile is private or deleted$1.485 per 1,000: Fetchin bills the lookuperror: "This LinkedIn profile is private or deleted."
Fetchin 429its rate limit, which all our customers sharefreeFetchin's answer after up to two retries, or error: "Try again in a minute"
402your balance can't cover the callfreeerror: "The Datacircle balance is too low for this call."
500, 502, 503 or 504the provider failed, or didn't answer within 45 secondsfreeerror: "Try again in a minute"
401your key is wrongfreean exception that stops the app: Datacircle answered 401 and our error

Up2Data takes $1 a day per account (421 profiles), with a shared daily limit for all customers, then answers 429 until 00:00 UTC. HarvestAPI has no daily limit. Fetchin has no daily limit either.

The tests we ran

  • We tested on October 12, 2026, with Java 17, 20 and 25, Maven 3.9.16, and LangChain4j 1.22.0: langchain4j and langchain4j-open-ai 1.22.0, langchain4j-mcp 1.22.0-beta32, the versions in LangChain4j's BOM.
  • We built each file and each variant above with mvn package and ran it, and ran each file once with mvn compile exec:java. OPENAI_BASE_URL pointed both files at a scripted stand-in for OpenAI's API. Without it, they call OpenAI.
  • We ran the method against a stand-in for our API that returned each answer in the table. We ran the MCP app against a stand-in for our MCP server with the tools our live server lists, and tried each error, a 50 second answer, a 65 second one, both timeouts, the long answer and a wrong tool name.
  • We didn't try a real Datacircle key or a real model.

Cost per 1,000 profiles

We charge your balance the prices on our pricing page, with no markup:

Our price per 1,000 profiles and daily limit, by provider
Up2DataHarvestAPIFetchin
Per 1,000 found$2.375$3.70$1.485
Per 1,000 not foundfree$2.30$1.485
Daily limit421 profiles per accountnonenone

Say your app looks up 1,000 profiles in a day, one call each, and the providers find every one. You pay us at most $1.86: $1.00 for 421 through Up2Data and $0.86 for the other 579 through Fetchin. Through Fetchin alone, the same 1,000 would cost less: $1.485. Calling Up2Data first costs less when more than about 37% of the profiles can't be found. Both files call Up2Data first because it bills nothing for a profile it can't find, and all our customers share Fetchin's rate limit. The model may call the tool more than once per question. Your model's provider bills you for its tokens.

The same tool in Java with Spring Boot: LinkedIn profiles in Spring AI. In agent frameworks for Python and TypeScript: LinkedIn profiles in LangChain, LinkedIn profiles in DSPy and LinkedIn profiles in the Vercel AI SDK. The same call from a script: with Python or with Node.js. In Claude, ChatGPT or Cursor: our LinkedIn MCP server. Other vendors' prices per 1,000: LinkedIn profile API pricing compared.

Questions

Does LangChain4j have a LinkedIn tool?

No. Connect our MCP server at https://api.datacircle.dev/mcp with LangChain4j's MCP client, from the langchain4j-mcp module. Or give your AI Service one @Tool method that sends POST {"url": "<the profile's LinkedIn URL>"} to https://api.datacircle.dev/v1/profiles/enrich, with the headers Authorization: Token <your key> and X-Data-Provider: up2data.

How do I send an API key to a remote MCP server in LangChain4j?

Call customHeaders on StreamableHttpMcpTransport.builder() with a map that holds the header Authorization: Bearer <your key>. The transport sends it with each request to the server.

What is the MCP tool timeout in LangChain4j?

60 seconds by default: toolExecutionTimeout on DefaultMcpClient.builder(), in langchain4j-mcp 1.22.0-beta32. At the timeout, the model read "There was a timeout executing the tool" in our test, and the app kept running.

How many tool calls does LangChain4j allow?

LangChain4j 1.22.0 allows 100 model answers with tool calls for each question, by default. One answer can ask for more than one call. Past the limit, the AI Service throws an exception. Set maxToolCallingRoundTrips on AiServices.builder() to lower it. maxToolCallingRoundTrips replaces maxSequentialToolsInvocations.

Why does my LangChain4j tool's parameter show up as arg0?

The Java compiler drops parameter names unless you pass it -parameters. In Maven, set maven.compiler.parameters to true. Or name the parameter with @P(name = "url").

How much does a LinkedIn profile cost?

$2.375 per 1,000 through Up2Data (a profile it can't find is free), $3.70 per 1,000 through HarvestAPI. $1.485 per 1,000 through Fetchin, a profile it can't find billed the same. HarvestAPI bills a profile it can't find at $2.30 per 1,000.

Is there a daily limit?

Up2Data takes $1 a day per account (421 profiles), with a shared daily limit for all customers, then answers 429 until 00:00 UTC. HarvestAPI has no daily limit. Fetchin has no daily limit either.

Is each request live, or cached?

Live. Each request goes to the provider and gets the profile as it is today.

Do I need a LinkedIn account?

No. You send the profile's URL with your Datacircle key: no LinkedIn login, no cookies, no browser.

What happens when my balance runs out?

A call your balance can't cover answers 402. Add funds, from $5, on your dashboard.

Get started

Sign up at datacircle.dev with your work email: a $5 credit, that's 2,105 LinkedIn profiles at $2.375 per 1,000.

Sign up
Ask AI about Datacircle

Each opens with our question