No description
Find a file
2025-08-10 23:33:32 -04:00
examples modifications to example, improved readme, added endpoints 2025-03-11 17:28:02 -04:00
src modifications to example, improved readme, added endpoints 2025-03-11 17:28:02 -04:00
tests modifications to example, improved readme, added endpoints 2025-03-11 17:28:02 -04:00
.gitignore added quick example, updated endpoints 2025-03-11 01:29:42 -04:00
dataforseo.nimble Update dataforseo.nimble 2025-03-11 18:05:52 -04:00
LICENSE Initial commit 2025-03-10 21:59:25 -04:00
README.md Update README.md 2025-08-10 23:33:32 -04:00

DataForSEO

A simple Nim REST client for the DataForSEO API (v3). No dependencies (stdlib only), supports both sync and async requests.

This library is intentially low-level and you must refer to the (well-designed) API documentation for proper usage- no hand holding, minimal abstractions. We are using DataForSEO as part of our tooling ecosytem for my SEO agency, SEO Science. We unfortunately do not have the time to make a high-level API and keep up with all changes DFSEO may make.

Under Active Development

Updates to this repository will be sporadic, as needed.

The API is expansive and covers endpoints we'll never use, such as the Baidu search engine. These endpoints will likely never be implemented by us.

This doesn't make the library unusable- quite the contrary. We designed the API to be extremely straightforward and simple. The Extending The API section details how you can create an endpoint that isn't currently supported. PRs are welcomed ;)

Installation

git clone https://github.com/Niminem/DataForSEO

or

nimble install dataforseo

Usage

# main.nim
import std/[strutils, json]
import dataforseo

let creds = readFile("creds.txt").splitWhitespace()
var client = newDFSClient(login=creds[0], password=creds[1])

let
    keyword = "seo consultant"
    data = %*[{"keywords": [keyword]}]
    response = client.fetch(ClickstreamGlobalSearchVolume, data)

assert response.code == Http200
let body = response.body.parseJson()
assert body["status_code"].getInt() == 20000

let globalsearchvol = body["tasks"][0]["result"][0]["items"][0]["search_volume"].getInt()
echo "Keyword: ", keyword
echo "Global Search Volume: ", globalsearchvol

client.close()
# nim c -r -d:ssl main.nim

This library supports synchronous and asynchronous requests via newDFSClient and newAsyncDFSClient respectively. Both clients are simply aliases for the http clients within std/httpclient.

There are currently only two basic functions:

# 1.) DataForSEO HTTPClient
proc newDFSClient*(login, password: string): DFSClient
proc newAsyncDFSClient*(login, password: string): AsyncDFSClient
# 2.) Performs requests for the client
proc fetch*(client: DFSClient | AsyncDFSClient; req: DFSRequest; taskData: JsonNode):
              Future[Response | AsyncResponse] {.multisync.}

When calling fetch, you'll be providing two key arguments:

  • req: DFSRequest
  • taskData: JsonNode

req is simply a tuple, with the type of http request and the endpoint:

type DFSRequest* = tuple[mthd: HttpMethod, endpoint: string]

We already defined many DFSRequest constants, named after the endpoint, like what we see above with ClickstreamGlobalSearchVolume.

Refer to the DataForSEO API documentation to see the name of the endpoint you need. If we don't have it yet, the section below will show you how to support it. Again, PRs are welcomed.

taskData is simply a JsonNode containing the data structure DataForSEO expects for that endpoint.

%*[{"keywords": [keyword]}] in the case above.

NOTE: All error handling, abstracting the response body, etc. falls on the developer.

Extending The API

src/dataforseo/endpoints/common exports the following to help you support wanted endpoints:

type DFSRequest* = tuple[mthd: HttpMethod, endpoint: string]
const EndpointBase* = "https://api.dataforseo.com/v3/"

All you need to do is create a DFSRequest based on your API.

An example:

const
    HistoricalBulkTrafficEstimation*: DFSRequest = (HttpPost,
        EndpointBase & "dataforseo_labs/google/historical_bulk_traffic_estimation/live")

Yep. That's it. Just make sure the appropriate HTTP request and endpoint matches, and you'll be able to use any endpoint this way.

# ...
let response = client.fetch(HistoricalBulkTrafficEstimation, myData)
# ...