# DataForSEO API Documentation

## Overview

**DataForSEO** is a comprehensive SEO data provider offering APIs for SERP data, keyword research, search volume, backlinks, domain analytics, and more. It's the go-to solution for building SEO tools and applications.

**Official Documentation**: https://docs.dataforseo.com/v3/  
**Pricing**: https://dataforseo.com/pricing  
**Dashboard**: https://app.dataforseo.com/  
**Help Center**: https://dataforseo.com/help-center

---

## Key Features

- **SERP API**: Real-time search engine results from Google, Bing, Yahoo, etc.
- **Keywords Data API**: Search volume, CPC, competition from Google Ads and Bing Ads
- **DataForSEO Labs API**: Historical search volume, keyword metrics, SERP data
- **Domain Analytics API**: Domain rankings, traffic estimates, competitor analysis
- **Backlinks API**: Backlink data, anchor texts, referring domains
- **OnPage API**: Website crawling, technical SEO analysis
- **Content Analysis API**: Content quality, readability, sentiment analysis
- **Business Data API**: Google My Business data, reviews, ratings

---

## Base URL

```
https://api.dataforseo.com/v3
```

---

## Authentication

DataForSEO uses **Basic Authentication** (HTTP Basic Auth).

### Getting API Credentials

1. Create an account at https://app.dataforseo.com/
2. Navigate to https://app.dataforseo.com/api-access
3. Get your **login** (email) and **API password** (auto-generated, different from account password)
4. Minimum payment: **$50**

### Using Basic Authentication

Credentials must be passed in the `Authorization` header as **Base64-encoded** `login:password`.

**Format**:
```
Authorization: Basic BASE64(login:password)
```

**Example**:
- Login: `user@example.com`
- Password: `api_password_123`
- Combined: `user@example.com:api_password_123`
- Base64: `dXNlckBleGFtcGxlLmNvbTphcGlfcGFzc3dvcmRfMTIz`
- Header: `Authorization: Basic dXNlckBleGFtcGxlLmNvbTphcGlfcGFzc3dvcmRfMTIz`

---

## Quick Start

### curl Example

```bash
# Set credentials
login="your_email@example.com"
password="your_api_password"
cred="$(printf ${login}:${password} | base64)"

# Make request
curl --location --request POST "https://api.dataforseo.com/v3/keywords_data/google_ads/search_volume/live" \
  --header "Authorization: Basic ${cred}" \
  --header "Content-Type: application/json" \
  --data-raw '[
    {
      "location_code": 2840,
      "keywords": [
        "seo tools",
        "keyword research",
        "backlink checker"
      ]
    }
  ]'
```

### PHP Example

```php
<?php

$login = 'your_email@example.com';
$password = 'your_api_password';

$post_array = [
    [
        'location_code' => 2840,
        'keywords' => [
            'seo tools',
            'keyword research',
            'backlink checker'
        ]
    ]
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.dataforseo.com/v3/keywords_data/google_ads/search_volume/live');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
curl_setopt($ch, CURLOPT_USERPWD, $login . ':' . $password);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_array));

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
print_r($result);
```

### Python Example

```python
import requests
import json
from base64 import b64encode

login = 'your_email@example.com'
password = 'your_api_password'

# Encode credentials
cred = b64encode(f'{login}:{password}'.encode()).decode()

# Request payload
payload = [
    {
        'location_code': 2840,
        'keywords': [
            'seo tools',
            'keyword research',
            'backlink checker'
        ]
    }
]

# Make request
response = requests.post(
    'https://api.dataforseo.com/v3/keywords_data/google_ads/search_volume/live',
    headers={
        'Authorization': f'Basic {cred}',
        'Content-Type': 'application/json'
    },
    data=json.dumps(payload)
)

result = response.json()
print(result)
```

---

## Main APIs for SEO Tool

### 1. Keywords Data API (Google Ads Search Volume)

Get search volume, CPC, competition, and monthly trends for keywords.

**Endpoint**: `POST /v3/keywords_data/google_ads/search_volume/live`

**Rate Limit**: 12 requests per minute

**Cost**: $0.025 per request (for up to 1000 keywords)

#### Request Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `keywords` | array | **Yes** | Array of keywords (max 1000) |
| `location_code` | integer | No | Location code (e.g., 2840 for USA) |
| `language_code` | string | No | Language code (e.g., "en") |
| `search_partners` | boolean | No | Include search partners. Default: false |
| `date_from` | string | No | Start date for historical data (YYYY-MM-DD) |

#### Example Request

```json
[
  {
    "location_code": 2840,
    "language_code": "en",
    "keywords": [
      "buy laptop",
      "cheap laptops for sale",
      "purchase laptop"
    ],
    "search_partners": false
  }
]
```

#### Response Format

```json
{
  "version": "0.1.20231117",
  "status_code": 20000,
  "status_message": "Ok.",
  "time": "1.9903 sec.",
  "cost": 0.025,
  "tasks_count": 1,
  "tasks_error": 0,
  "tasks": [
    {
      "id": "11301935-1535-0367-0000-b44e4432f0be",
      "status_code": 20000,
      "status_message": "Ok.",
      "time": "1.8689 sec.",
      "cost": 0.025,
      "result_count": 3,
      "result": [
        {
          "keyword": "buy laptop",
          "location_code": 2840,
          "language_code": "en",
          "search_partners": false,
          "competition": "HIGH",
          "competition_index": 100,
          "search_volume": 2900,
          "low_top_of_page_bid": 1.69,
          "high_top_of_page_bid": 10.04,
          "cpc": 7.95,
          "monthly_searches": [
            {
              "year": 2023,
              "month": 10,
              "search_volume": 2400
            },
            {
              "year": 2023,
              "month": 9,
              "search_volume": 2900
            }
          ]
        }
      ]
    }
  ]
}
```

#### Key Response Fields

| Field | Type | Description |
|-------|------|-------------|
| `keyword` | string | The keyword |
| `search_volume` | integer | Average monthly search volume |
| `competition` | string | Competition level: LOW, MEDIUM, HIGH |
| `competition_index` | integer | Competition index (0-100) |
| `cpc` | float | Average cost-per-click in USD |
| `low_top_of_page_bid` | float | Low bid for top of page |
| `high_top_of_page_bid` | float | High bid for top of page |
| `monthly_searches` | array | Historical monthly search volumes |

---

### 2. DataForSEO Labs API (Historical Search Volume)

Get historical search volume data since 2019, along with SERP info.

**Endpoint**: `POST /v3/dataforseo_labs/google/historical_search_volume/live`

**Rate Limit**: 2000 requests per minute

**Cost**: ~$0.0051 per keyword

#### Request Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `keywords` | array | **Yes** | Array of keywords (max 700) |
| `location_code` | integer | No | Location code (e.g., 2840 for USA) |
| `language_name` | string | No | Language name (e.g., "English") |
| `include_serp_info` | boolean | No | Include SERP data. Default: false |

#### Example Request

```json
[
  {
    "keywords": [
      "phone",
      "watch"
    ],
    "language_name": "English",
    "location_code": 2840,
    "include_serp_info": true
  }
]
```

#### Response Format

```json
{
  "status_code": 20000,
  "cost": 0.0102,
  "tasks": [
    {
      "result": [
        {
          "items": [
            {
              "keyword": "phone",
              "location_code": 2840,
              "language_code": "en",
              "keyword_info": {
                "last_updated_time": "2024-08-11 13:24:34 +00:00",
                "competition": 1,
                "competition_level": "HIGH",
                "cpc": 5.98,
                "search_volume": 368000,
                "low_top_of_page_bid": 3.08,
                "high_top_of_page_bid": 10.5,
                "monthly_searches": [
                  {
                    "year": 2024,
                    "month": 7,
                    "search_volume": 450000
                  },
                  {
                    "year": 2024,
                    "month": 6,
                    "search_volume": 368000
                  }
                ]
              },
              "serp_info": {
                "se_results_count": "1234567890",
                "last_updated_time": "2024-08-11 12:00:00 +00:00"
              }
            }
          ]
        }
      ]
    }
  ]
}
```

---

### 3. Keywords For Site

Get keyword suggestions based on a competitor's domain.

**Endpoint**: `POST /v3/keywords_data/google_ads/keywords_for_site/live`

**Rate Limit**: 12 requests per minute

**Cost**: $0.05 per request

#### Example Request

```json
[
  {
    "location_code": 2840,
    "language_code": "en",
    "site": "example.com"
  }
]
```

---

### 4. Keywords For Keywords

Get related keyword suggestions based on seed keywords.

**Endpoint**: `POST /v3/keywords_data/google_ads/keywords_for_keywords/live`

**Rate Limit**: 12 requests per minute

**Cost**: $0.05 per request

#### Example Request

```json
[
  {
    "location_code": 2840,
    "language_code": "en",
    "keywords": [
      "seo tools"
    ]
  }
]
```

---

## PHP Laravel Service Implementation

### DataForSEO Service Class

```php
<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;

class DataForSEOService
{
    private $login;
    private $password;
    private $baseUrl = 'https://api.dataforseo.com/v3';
    
    public function __construct()
    {
        $this->login = config('services.dataforseo.login');
        $this->password = config('services.dataforseo.password');
    }
    
    /**
     * Get search volume for keywords
     */
    public function getSearchVolume(array $keywords, int $locationCode = 2840, string $languageCode = 'en')
    {
        $endpoint = '/keywords_data/google_ads/search_volume/live';
        
        $payload = [
            [
                'location_code' => $locationCode,
                'language_code' => $languageCode,
                'keywords' => $keywords,
                'search_partners' => false,
            ]
        ];
        
        return $this->makeRequest($endpoint, $payload);
    }
    
    /**
     * Get historical search volume with SERP info
     */
    public function getHistoricalSearchVolume(array $keywords, int $locationCode = 2840, string $languageName = 'English')
    {
        $endpoint = '/dataforseo_labs/google/historical_search_volume/live';
        
        $payload = [
            [
                'keywords' => $keywords,
                'language_name' => $languageName,
                'location_code' => $locationCode,
                'include_serp_info' => true,
            ]
        ];
        
        return $this->makeRequest($endpoint, $payload);
    }
    
    /**
     * Get keywords for a competitor site
     */
    public function getKeywordsForSite(string $site, int $locationCode = 2840, string $languageCode = 'en')
    {
        $endpoint = '/keywords_data/google_ads/keywords_for_site/live';
        
        $payload = [
            [
                'location_code' => $locationCode,
                'language_code' => $languageCode,
                'site' => $site,
            ]
        ];
        
        return $this->makeRequest($endpoint, $payload);
    }
    
    /**
     * Get related keywords
     */
    public function getRelatedKeywords(array $keywords, int $locationCode = 2840, string $languageCode = 'en')
    {
        $endpoint = '/keywords_data/google_ads/keywords_for_keywords/live';
        
        $payload = [
            [
                'location_code' => $locationCode,
                'language_code' => $languageCode,
                'keywords' => $keywords,
            ]
        ];
        
        return $this->makeRequest($endpoint, $payload);
    }
    
    /**
     * Make API request
     */
    private function makeRequest(string $endpoint, array $payload)
    {
        $url = $this->baseUrl . $endpoint;
        
        try {
            $response = Http::withBasicAuth($this->login, $this->password)
                ->withHeaders([
                    'Content-Type' => 'application/json',
                ])
                ->timeout(60)
                ->post($url, $payload);
            
            if ($response->successful()) {
                $result = $response->json();
                
                // Log usage
                $this->logUsage($endpoint, $result);
                
                return $result;
            }
            
            throw new \Exception('DataForSEO API error: ' . $response->body());
            
        } catch (\Exception $e) {
            Log::error('DataForSEO API request failed', [
                'endpoint' => $endpoint,
                'error' => $e->getMessage(),
            ]);
            
            throw $e;
        }
    }
    
    /**
     * Log API usage for cost tracking
     */
    private function logUsage(string $endpoint, array $result)
    {
        $cost = $result['cost'] ?? 0;
        $time = $result['time'] ?? 0;
        
        \App\Models\ApiUsageLog::create([
            'service' => 'dataforseo',
            'endpoint' => $endpoint,
            'cost' => $cost,
            'response_time' => $time,
        ]);
        
        Log::info('DataForSEO API usage', [
            'endpoint' => $endpoint,
            'cost' => $cost,
            'time' => $time,
        ]);
    }
    
    /**
     * Extract search volume data from response
     */
    public function extractSearchVolumeData(array $response)
    {
        if (!isset($response['tasks'][0]['result'])) {
            return [];
        }
        
        $keywords = [];
        
        foreach ($response['tasks'][0]['result'] as $item) {
            $keywords[] = [
                'keyword' => $item['keyword'],
                'search_volume' => $item['search_volume'] ?? 0,
                'cpc' => $item['cpc'] ?? 0,
                'competition' => $item['competition'] ?? 'UNKNOWN',
                'competition_index' => $item['competition_index'] ?? 0,
                'low_bid' => $item['low_top_of_page_bid'] ?? 0,
                'high_bid' => $item['high_top_of_page_bid'] ?? 0,
            ];
        }
        
        return $keywords;
    }
}
```

### Usage in Laravel Job

```php
<?php

namespace App\Jobs;

use App\Services\DataForSEOService;
use App\Models\KeywordRecommendation;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;

class FetchSearchVolumeData implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
    protected $keywords;
    protected $pageId;
    
    public function __construct(array $keywords, int $pageId)
    {
        $this->keywords = $keywords;
        $this->pageId = $pageId;
    }
    
    public function handle(DataForSEOService $dataforseo)
    {
        try {
            // Batch keywords (max 1000 per request)
            $batches = array_chunk($this->keywords, 1000);
            
            foreach ($batches as $batch) {
                // Get search volume
                $response = $dataforseo->getSearchVolume($batch);
                
                // Extract data
                $keywordData = $dataforseo->extractSearchVolumeData($response);
                
                // Save to database
                foreach ($keywordData as $data) {
                    KeywordRecommendation::updateOrCreate(
                        [
                            'page_id' => $this->pageId,
                            'keyword' => $data['keyword'],
                        ],
                        [
                            'search_volume' => $data['search_volume'],
                            'cpc' => $data['cpc'],
                            'competition' => $data['competition'],
                            'competition_index' => $data['competition_index'],
                            'status' => 'completed',
                        ]
                    );
                }
                
                // Rate limiting: 12 requests per minute = 5 seconds per request
                if (count($batches) > 1) {
                    sleep(5);
                }
            }
            
            Log::info('Search volume data fetched successfully', [
                'page_id' => $this->pageId,
                'keywords_count' => count($this->keywords),
            ]);
            
        } catch (\Exception $e) {
            Log::error('Failed to fetch search volume data', [
                'page_id' => $this->pageId,
                'error' => $e->getMessage(),
            ]);
            
            throw $e;
        }
    }
}
```

### Configuration (.env)

```env
# DataForSEO Configuration
DATAFORSEO_LOGIN=your_email@example.com
DATAFORSEO_PASSWORD=your_api_password
```

### Config File (config/services.php)

```php
'dataforseo' => [
    'login' => env('DATAFORSEO_LOGIN'),
    'password' => env('DATAFORSEO_PASSWORD'),
],
```

---

## Location and Language Codes

### Common Location Codes

| Location | Code |
|----------|------|
| United States | 2840 |
| United Kingdom | 2826 |
| Canada | 2124 |
| Australia | 2036 |
| Germany | 2276 |
| France | 2250 |

**Full list**: https://docs.dataforseo.com/v3/keywords_data/google_ads/locations/

### Common Language Codes

| Language | Code |
|----------|------|
| English | en |
| Spanish | es |
| French | fr |
| German | de |
| Italian | it |

**Full list**: https://docs.dataforseo.com/v3/keywords_data/google_ads/languages/

---

## Rate Limits

| API | Rate Limit | Notes |
|-----|------------|-------|
| Keywords Data (Google Ads) | 12 requests/minute | Live endpoints |
| DataForSEO Labs | 2000 requests/minute | Most endpoints |
| SERP API | Varies by endpoint | Check docs |

**Best Practice**: Add 5-second delays between requests for Google Ads endpoints.

---

## Pricing

### Pay-As-You-Go Model

- **Minimum payment**: $50
- **No subscription fees**
- **Pay only for what you use**

### Common Endpoint Costs

| Endpoint | Cost | Notes |
|----------|------|-------|
| Google Ads Search Volume (Live) | $0.025 | Up to 1000 keywords |
| Historical Search Volume | $0.0051/keyword | DataForSEO Labs |
| Keywords For Site | $0.05 | Per request |
| Keywords For Keywords | $0.05 | Per request |
| SERP API | $0.0006 - $0.006 | Varies by search engine |

### Cost Estimation for SEO Tool

**Scenario**: Analyze 100 pages with 20 keywords each (2000 keywords total)

- **Search Volume**: 2 requests × $0.025 = **$0.05**
- **Historical Data**: 2000 keywords × $0.0051 = **$10.20**
- **Total**: ~**$10.25** for 100 pages

**Recommendation**: Use Google Ads Search Volume endpoint for cost efficiency.

---

## Error Handling

### Common Status Codes

| Code | Meaning | Action |
|------|---------|--------|
| 20000 | Success | Process result |
| 40101 | Authentication failed | Check credentials |
| 40301 | Insufficient funds | Add credits |
| 50000 | Internal server error | Retry with backoff |

### PHP Error Handling Example

```php
try {
    $response = $dataforseo->getSearchVolume($keywords);
    
    if ($response['status_code'] !== 20000) {
        throw new \Exception('API error: ' . $response['status_message']);
    }
    
    // Process result
    
} catch (\Exception $e) {
    if (str_contains($e->getMessage(), 'Authentication')) {
        Log::error('DataForSEO authentication failed');
        // Alert admin
    } elseif (str_contains($e->getMessage(), 'Insufficient funds')) {
        Log::error('DataForSEO insufficient funds');
        // Alert admin to add credits
    } else {
        Log::error('DataForSEO API error', ['error' => $e->getMessage()]);
        // Retry with exponential backoff
        sleep(5);
        return $dataforseo->getSearchVolume($keywords);
    }
}
```

---

## Best Practices

### 1. Batch Keywords

```php
// Process up to 1000 keywords per request
$batches = array_chunk($keywords, 1000);

foreach ($batches as $batch) {
    $response = $dataforseo->getSearchVolume($batch);
    // Process response
    sleep(5); // Rate limiting
}
```

### 2. Cache Results

```php
public function getSearchVolumeWithCache(array $keywords)
{
    $cacheKey = 'search_volume_' . md5(implode(',', $keywords));
    
    return Cache::remember($cacheKey, now()->addDays(30), function () use ($keywords) {
        return $this->getSearchVolume($keywords);
    });
}
```

### 3. Monitor Costs

```php
// Get daily cost
$dailyCost = ApiUsageLog::where('service', 'dataforseo')
    ->whereDate('created_at', today())
    ->sum('cost');

// Alert if over budget
if ($dailyCost > config('services.dataforseo.daily_budget')) {
    // Send alert, pause processing
}
```

### 4. Use Location Targeting

```php
// Target specific location for accurate search volume
$response = $dataforseo->getSearchVolume($keywords, 2840, 'en'); // USA, English
```

---

## Additional Resources

- **Official Documentation**: https://docs.dataforseo.com/v3/
- **Pricing**: https://dataforseo.com/pricing
- **Help Center**: https://dataforseo.com/help-center
- **API Status**: https://status.dataforseo.com/
- **Support**: support@dataforseo.com
- **GitHub Examples**: https://github.com/dataforseo
- **Postman Collection**: https://docs.dataforseo.com/v3/#postman-examples

---

## Summary for SEO Tool Implementation

### Why Use DataForSEO?

1. **Accurate Search Volume**: Based on Google Ads API
2. **Historical Data**: Search volume since 2019
3. **Comprehensive Metrics**: CPC, competition, monthly trends
4. **Batch Processing**: Up to 1000 keywords per request
5. **Cost-Effective**: Pay-as-you-go, no subscription

### Recommended Workflow

1. **Extract Keywords** from page content using OpenAI/OpenRouter
2. **Get Search Volume** using DataForSEO Google Ads API
3. **Fetch Historical Data** using DataForSEO Labs API (optional)
4. **Analyze Competitors** using Keywords For Site endpoint
5. **Find Related Keywords** using Keywords For Keywords endpoint
6. **Store Results** in database with metrics

### Cost Optimization

- Use **Google Ads Search Volume** endpoint (cheapest: $0.025 per 1000 keywords)
- Batch keywords (up to 1000 per request)
- Cache results for 30 days
- Monitor daily spending
- Set budget alerts

### Integration with AI

```php
// Step 1: Extract keywords with AI
$aiKeywords = $openai->extractKeywords($pageContent);

// Step 2: Get search volume from DataForSEO
$keywords = array_column($aiKeywords['keywords'], 'keyword');
$searchVolumeData = $dataforseo->getSearchVolume($keywords);

// Step 3: Merge AI analysis with search volume
foreach ($aiKeywords['keywords'] as &$keyword) {
    $svData = $this->findKeywordData($keyword['keyword'], $searchVolumeData);
    $keyword['search_volume'] = $svData['search_volume'] ?? 0;
    $keyword['cpc'] = $svData['cpc'] ?? 0;
    $keyword['competition'] = $svData['competition'] ?? 'UNKNOWN';
}

// Step 4: Save enriched data
$this->saveKeywordRecommendations($aiKeywords['keywords'], $pageId);
```

**DataForSEO provides the essential search volume and competition data that AI models cannot generate on their own.**

