laravel-sdk maintained by refinder
Refinder Laravel SDK
Overview
The Refinder Laravel SDK is a first-party Laravel package that integrates your application with the Refinder AI Tools platform. It provides:
- A clean Facade (
Refinder) for quick access to all features - Fully typed DTOs for all requests and responses
- Automatic authentication via API key (
X-API-Keyheader) - Built-in error handling with a custom exception hierarchy
- Event dispatching for tool executions (success & failure)
- Caching support for identical tool inputs
- Usage tracking helpers for monitoring quotas
- Full IDE autocompletion support via PHPDoc annotations
What Does the Refinder API Do?
Refinder AI Tools is an AI middleware platform that provides AI-powered tools as a service:
- Abstracts multiple LLM providers (OpenAI, DeepSeek, Alibaba Cloud Qwen, etc.) behind a unified interface
- Provides pluggable AI tools (starting with SEO)
- Manages subscriptions, usage quotas, and rate limiting
- Returns structured JSON outputs from all tools
Requirements
| Requirement | Version |
|---|---|
| PHP | >= 8.2 |
| Laravel | 11.x or 12.x |
| Guzzle | >= 7.0 |
Installation
composer require refinder/laravel-sdk
The package auto-registers its service provider and facade via Laravel's package discovery.
Publish Configuration
php artisan vendor:publish --tag=refinder-config
This creates config/refinder.php with all available options.
Environment Variables
Add the following to your .env file:
REFINDER_API_KEY=rfnd_your_api_key_here
REFINDER_BASE_URL=https://api.refinder.ai/api/v1
REFINDER_TIMEOUT=120
REFINDER_RETRIES=2
REFINDER_CACHE_ENABLED=false
REFINDER_CACHE_TTL=60
REFINDER_EVENTS=true
Required: Only
REFINDER_API_KEYis required. All other values have sensible defaults.
Quick Start
use Refinder\LaravelSdk\Facades\Refinder;
// Execute SEO analysis
$result = Refinder::seo()->execute([
'content' => 'Your article content here...',
'content_type' => 'article',
'language' => 'en',
]);
// Access the results
echo $result->output->metaTitle; // "Optimized SEO Title..."
echo $result->output->metaDescription; // "Meta description..."
echo $result->output->seoScore->overall; // 75 (0-100)
// Check if score is passing (>= 60)
if ($result->output->seoScore->isPassing()) {
echo "SEO score is good!";
}
Configuration
The full configuration file (config/refinder.php):
return [
// Your API key (required)
'api_key' => env('REFINDER_API_KEY'),
// API base URL
'base_url' => env('REFINDER_BASE_URL', 'https://api.refinder.ai/api/v1'),
// Request timeout in seconds (tool executions can take 10-30s)
'timeout' => env('REFINDER_TIMEOUT', 120),
// Retry on 5xx/timeout errors (never retries 4xx)
'retries' => env('REFINDER_RETRIES', 2),
'retry_delay_ms' => env('REFINDER_RETRY_DELAY', 1000),
// Response caching for identical inputs
'cache' => [
'enabled' => env('REFINDER_CACHE_ENABLED', false),
'ttl' => env('REFINDER_CACHE_TTL', 60), // minutes
'prefix' => 'refinder_',
],
// Dispatch Laravel events after tool executions
'events' => env('REFINDER_EVENTS', true),
// Log channel (null = default channel)
'log_channel' => env('REFINDER_LOG_CHANNEL', null),
];
SEO Tool
The SEO tool is the primary feature of the Refinder platform. It analyzes content and generates comprehensive SEO recommendations.
Input Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
content |
string |
Yes | — | Text content to analyze (min 10 chars) |
url |
string |
No | null |
Page URL for additional context |
content_type |
string |
No | "page" |
One of: article, product, service, page, blog |
language |
string |
No | "en" |
Target language code (e.g., en, ar, fr) |
depth |
string |
No | "advanced" |
Analysis depth: basic, advanced, technical |
target_keywords |
array |
No | [] |
Specific keywords to optimize around |
brand_name |
string |
No | null |
Brand name for meta titles/headings |
industry |
string |
No | null |
Industry context for better optimization |
Depth Levels
| Level | Includes |
|---|---|
| basic | Meta title, meta description, primary keywords |
| advanced | Everything in basic + secondary/long-tail keywords, content analysis, heading suggestions, optimization suggestions, SEO scores |
| technical | Everything in advanced + schema markup, Open Graph tags, canonical URLs, content gap analysis |
Using the SeoInput DTO
use Refinder\LaravelSdk\DTOs\SeoInput;
use Refinder\LaravelSdk\Facades\Refinder;
$input = new SeoInput(
content: $article->body,
url: route('articles.show', $article),
contentType: 'article',
language: 'en',
depth: 'advanced',
targetKeywords: ['AI tools', 'machine learning'],
brandName: 'MyBrand',
industry: 'Technology',
);
$result = Refinder::seo()->execute($input);
Shorthand Methods
// Basic analysis
$result = Refinder::seo()->basic($content, 'en');
// Advanced analysis (default)
$result = Refinder::seo()->advanced($content, 'en');
// Full technical analysis
$result = Refinder::seo()->technical($content, 'en');
Working with SEO Output
$result = Refinder::seo()->execute(['content' => $text]);
$seo = $result->output;
// Meta tags
echo $seo->metaTitle; // string (50-60 chars)
echo $seo->metaDescription; // string (150-160 chars)
// Keywords
$seo->keywords->primary; // ['keyword1', 'keyword2', ...]
$seo->keywords->secondary; // ['keyword3', 'keyword4', ...]
$seo->keywords->longTail; // ['long tail phrase', ...]
$seo->keywords->all(); // All keywords merged
// Content Analysis
$seo->contentAnalysis->readabilityScore; // 'excellent', 'good', 'needs_improvement'
$seo->contentAnalysis->wordCountAssessment; // string
$seo->contentAnalysis->contentQuality; // 'high', 'medium', 'low'
$seo->contentAnalysis->keyTopicsCovered; // ['topic1', ...]
// SEO Score (0-100)
$seo->seoScore->overall; // 75
$seo->seoScore->content; // 70
$seo->seoScore->keywords; // 80
$seo->seoScore->structure; // 75
$seo->seoScore->isPassing(); // true (>= 60)
// Optimization Suggestions
foreach ($seo->optimizationSuggestions as $suggestion) {
echo $suggestion->category; // 'content', 'technical', 'structure', 'keywords'
echo $suggestion->priority; // 'high', 'medium', 'low'
echo $suggestion->suggestion; // "Add structured data markup"
echo $suggestion->details; // "Implementing JSON-LD..."
$suggestion->isHighPriority(); // bool
}
// Heading Structure
echo $seo->headingStructure->suggestedH1; // string
$seo->headingStructure->suggestedH2s; // array of strings
$seo->headingStructure->suggestedH3s; // array of strings
// Execution metadata
echo $result->id; // UUID
echo $result->status; // 'completed'
echo $result->usage->totalTokens; // 1276
echo $result->usage->executionTimeSeconds(); // 14.5
echo $result->model->name; // 'Qwen Plus'
echo $result->model->provider; // 'alibaba'
API Reference
Platform Information
$platform = Refinder::me();
echo $platform->id; // 1
echo $platform->name; // "My Platform"
echo $platform->slug; // "my-platform"
echo $platform->website; // "https://example.com"
echo $platform->description; // "Platform description"
echo $platform->isActive; // true
echo $platform->createdAt; // "2026-02-08T09:00:00+00:00"
Subscription Details
$sub = Refinder::subscription();
echo $sub->plan->name; // "Pro"
echo $sub->plan->slug; // "pro"
echo $sub->plan->maxRequestsPerMonth; // 1000
echo $sub->plan->maxRequestsPerDay; // 100
echo $sub->plan->features; // ['advanced_seo', 'api_access', ...]
echo $sub->status; // "active"
$sub->isActive(); // true
$sub->isOnTrial(); // false
Usage Statistics
// Current month usage
$usage = Refinder::usage();
// Custom date range
$usage = Refinder::usage('2026-01-01', '2026-01-31');
echo $usage->totalRequests; // 47
echo $usage->totalTokens; // 62340
echo $usage->totalCost; // 0.12468
echo $usage->periodFrom; // "2026-02-01"
echo $usage->periodTo; // "2026-02-08"
// Daily breakdown
foreach ($usage->daily as $day) {
echo $day->date; // "2026-02-08"
echo $day->tool; // "seo"
echo $day->requestsCount; // 12
echo $day->tokensUsed; // 15600
echo $day->estimatedCost; // 0.0312
}
Execution History
// List executions (paginated)
$executions = Refinder::executions(perPage: 10, page: 1);
echo $executions->total; // 47
echo $executions->currentPage; // 1
echo $executions->lastPage; // 5
echo $executions->hasMorePages(); // true
foreach ($executions->items as $exec) {
echo $exec->id; // UUID
echo $exec->tool; // "seo"
echo $exec->status; // "completed"
echo $exec->usage->totalTokens; // 1276
echo $exec->createdAt; // ISO 8601 string
}
// Get specific execution
$exec = Refinder::execution('d47181d4-d26b-4224-a7f7-4c973b66f8fa');
if ($exec->isCompleted()) {
echo $exec->output->metaTitle;
}
Available Tools
$tools = Refinder::tools();
// Returns array of tool definitions
Error Handling
The SDK throws specific exception types for different error scenarios:
RefinderException (base)
├── AuthenticationException (HTTP 401)
├── SubscriptionException (HTTP 403)
├── ValidationException (HTTP 422)
├── RateLimitException (HTTP 429)
├── ToolException (HTTP 500/502)
└── ConnectionException (Network errors)
Exception Properties
| Exception | Extra Properties |
|---|---|
RefinderException |
errorCode, httpStatus |
ValidationException |
errors (array of field errors) |
RateLimitException |
limit, current |
ToolException |
executionId (UUID for debugging) |
Error Handling Example
use Refinder\LaravelSdk\Exceptions\AuthenticationException;
use Refinder\LaravelSdk\Exceptions\RateLimitException;
use Refinder\LaravelSdk\Exceptions\SubscriptionException;
use Refinder\LaravelSdk\Exceptions\ToolException;
use Refinder\LaravelSdk\Exceptions\ValidationException;
use Refinder\LaravelSdk\Facades\Refinder;
try {
$result = Refinder::seo()->execute(['content' => $text]);
return response()->json([
'seo' => $result->output,
'tokens' => $result->usage->totalTokens,
]);
} catch (ValidationException $e) {
// Input validation failed (422)
return response()->json(['error' => $e->errors], 422);
} catch (RateLimitException $e) {
// Quota exceeded (429)
return response()->json([
'error' => 'Rate limit reached',
'limit' => $e->limit,
'current' => $e->current,
], 429);
} catch (SubscriptionException $e) {
// Subscription issue (403)
return response()->json(['error' => $e->getMessage()], 403);
} catch (AuthenticationException $e) {
// Invalid API key (401)
Log::critical('Refinder API key is invalid!', ['code' => $e->errorCode]);
return response()->json(['error' => 'Service unavailable'], 503);
} catch (ToolException $e) {
// AI processing failed (500/502)
Log::error('Tool failed', ['execution_id' => $e->executionId]);
return response()->json(['error' => 'Please retry'], 502);
}
Error Codes Reference
| HTTP | Code | Description |
|---|---|---|
| 401 | MISSING_API_KEY |
No API key in request header |
| 401 | INVALID_API_KEY |
API key doesn't match any active key |
| 401 | API_KEY_EXPIRED |
API key has expired |
| 403 | PLATFORM_INACTIVE |
Platform is disabled |
| 403 | NO_ACTIVE_SUBSCRIPTION |
No active subscription |
| 403 | SUBSCRIPTION_EXPIRED |
Subscription has expired |
| 403 | TOOL_NOT_ALLOWED |
Plan doesn't include this tool |
| 404 | TOOL_NOT_FOUND |
Tool slug doesn't exist |
| 404 | EXECUTION_NOT_FOUND |
Execution UUID not found |
| 422 | VALIDATION_ERROR |
Input validation failed |
| 429 | DAILY_LIMIT_EXCEEDED |
Daily quota exceeded |
| 429 | MONTHLY_LIMIT_EXCEEDED |
Monthly quota exceeded |
| 500 | TOOL_ERROR |
Tool execution error |
| 502 | LLM_ERROR |
AI provider returned an error |
Events
When config('refinder.events') is true (default), the package dispatches Laravel events:
ToolExecuted
Dispatched after a successful tool execution:
use Refinder\LaravelSdk\Events\ToolExecuted;
Event::listen(ToolExecuted::class, function (ToolExecuted $event) {
Log::info("Refinder tool executed", [
'tool' => $event->tool, // "seo"
'execution_id' => $event->executionId,
'tokens' => $event->tokensUsed,
'time_ms' => $event->executionTimeMs,
]);
});
ToolExecutionFailed
Dispatched after a failed tool execution:
use Refinder\LaravelSdk\Events\ToolExecutionFailed;
Event::listen(ToolExecutionFailed::class, function (ToolExecutionFailed $event) {
Log::error("Refinder tool failed", [
'tool' => $event->tool,
'error_code' => $event->errorCode,
'message' => $event->errorMessage,
'execution_id' => $event->executionId,
]);
});
RateLimitApproaching
Dispatched when usage approaches the limit:
use Refinder\LaravelSdk\Events\RateLimitApproaching;
Event::listen(RateLimitApproaching::class, function (RateLimitApproaching $event) {
Log::warning("Approaching rate limit", [
'type' => $event->limitType, // "daily" or "monthly"
'limit' => $event->limit,
'current' => $event->current,
'percentage' => $event->percentageUsed,
]);
});
Caching
Enable response caching for identical tool inputs:
REFINDER_CACHE_ENABLED=true
REFINDER_CACHE_TTL=60
- Uses Laravel's default cache driver
- Cache keys are generated from
prefix + tool_slug + md5(input) - Only successful executions are cached (never failures)
- TTL is in minutes
Rate Limiting & Quotas
The Refinder API enforces quotas based on your subscription plan:
| Limit | Reset Period |
|---|---|
| Daily | Midnight UTC |
| Monthly | 1st of month |
Checking Quota Usage
$usage = Refinder::usage();
$sub = Refinder::subscription();
$used = $usage->totalRequests;
$limit = $sub->plan->maxRequestsPerMonth;
$remaining = $limit - $used;
$percentage = ($used / $limit) * 100;
echo "Used {$used}/{$limit} ({$percentage}%) this month.";
echo "{$remaining} requests remaining.";
Testing
The SDK provides a RefinderFake class for testing:
Basic Fake
use Refinder\LaravelSdk\Facades\Refinder;
public function test_seo_analysis()
{
Refinder::fake();
// Your code calls the SDK as normal
$result = Refinder::seo()->execute(['content' => 'Test content for SEO']);
// Results come from the fake
$this->assertEquals('completed', $result->status);
$this->assertNotNull($result->output->metaTitle);
$this->assertTrue($result->output->seoScore->isPassing());
// Assert the tool was called
Refinder::assertToolExecuted('seo');
Refinder::assertToolExecutedCount('seo', 1);
}
Custom Fake Responses
Refinder::fake();
Refinder::fakeToolExecution('seo', [
'meta_title' => 'Custom Test Title',
'meta_description' => 'Custom test description.',
'keywords' => [
'primary' => ['custom keyword'],
'secondary' => ['test'],
'long_tail' => ['custom long tail'],
],
'content_analysis' => [
'readability_score' => 'excellent',
'word_count_assessment' => 'Perfect length.',
'content_quality' => 'high',
'key_topics_covered' => ['testing'],
],
'optimization_suggestions' => [],
'heading_structure' => [
'suggested_h1' => 'Custom H1',
'suggested_h2s' => ['Custom H2'],
'suggested_h3s' => [],
],
'seo_score' => [
'overall' => 95,
'content' => 90,
'keywords' => 95,
'structure' => 95,
],
]);
$result = Refinder::seo()->execute(['content' => 'Any content']);
$this->assertEquals('Custom Test Title', $result->output->metaTitle);
$this->assertEquals(95, $result->output->seoScore->overall);
Assertions
Refinder::fake();
// Assert tool was executed
Refinder::assertToolExecuted('seo');
// Assert exact count
Refinder::assertToolExecutedCount('seo', 3);
// Assert nothing was executed
Refinder::assertNothingExecuted();
Real-World Examples
E-Commerce Product SEO
$product = Product::find(1);
$result = Refinder::seo()->execute([
'content' => $product->description,
'url' => route('products.show', $product),
'content_type' => 'product',
'brand_name' => $product->brand->name,
'industry' => $product->category->name,
'target_keywords' => $product->tags->pluck('name')->toArray(),
]);
if ($result->isCompleted()) {
$product->seoMeta()->updateOrCreate([], [
'title' => $result->output->metaTitle,
'description' => $result->output->metaDescription,
'keywords' => $result->output->keywords->all(),
'h1' => $result->output->headingStructure->suggestedH1,
'score' => $result->output->seoScore->overall,
'analyzed_at' => now(),
]);
}
Batch Processing with Queue
use Refinder\LaravelSdk\Facades\Refinder;
use Refinder\LaravelSdk\Exceptions\RateLimitException;
class AnalyzeArticleSeo implements ShouldQueue
{
public int $tries = 3;
public int $backoff = 60;
public function __construct(public Article $article) {}
public function handle(): void
{
try {
$result = Refinder::seo()->execute([
'content' => $this->article->body,
'content_type' => 'article',
'language' => $this->article->locale,
'depth' => 'advanced',
]);
$this->article->update([
'seo_title' => $result->output->metaTitle,
'seo_description' => $result->output->metaDescription,
'seo_score' => $result->output->seoScore->overall,
'seo_analyzed_at' => now(),
]);
} catch (RateLimitException $e) {
$this->release(300); // retry in 5 minutes
}
}
}
// Dispatch for all unanalyzed articles
Article::whereNull('seo_analyzed_at')->chunk(50, function ($articles) {
foreach ($articles as $article) {
AnalyzeArticleSeo::dispatch($article);
}
});
Admin Dashboard Integration
// Get platform info
$platform = Refinder::me();
echo "Platform: {$platform->name} | Active: " . ($platform->isActive ? 'Yes' : 'No');
// Get subscription
$sub = Refinder::subscription();
echo "Plan: {$sub->plan->name} | Monthly: {$sub->plan->maxRequestsPerMonth}";
// Get usage
$usage = Refinder::usage();
echo "Requests: {$usage->totalRequests} | Tokens: {$usage->totalTokens}";
echo "Cost: \${$usage->totalCost}";
// Browse execution history
$executions = Refinder::executions(perPage: 10);
foreach ($executions->items as $exec) {
echo "{$exec->id} | {$exec->tool} | {$exec->status} | {$exec->usage->totalTokens} tokens";
}
Package Structure
refinder-laravel-sdk/
├── src/
│ ├── RefinderServiceProvider.php # Auto-discovered service provider
│ ├── RefinderClient.php # Core HTTP client
│ ├── RefinderManager.php # Main manager (Facade target)
│ ├── Facades/
│ │ └── Refinder.php # Facade with fake() support
│ ├── Tools/
│ │ ├── BaseTool.php # Abstract base with caching/events
│ │ └── SeoTool.php # SEO tool wrapper
│ ├── DTOs/
│ │ ├── SeoInput.php # Input DTO
│ │ ├── SeoOutput.php # Full output DTO
│ │ ├── SeoKeywords.php # Keywords container
│ │ ├── SeoScore.php # Score with isPassing()
│ │ ├── SeoSuggestion.php # Single suggestion
│ │ ├── SeoContentAnalysis.php # Content analysis data
│ │ ├── SeoHeadingStructure.php # Heading suggestions
│ │ ├── ToolExecution.php # Execution result wrapper
│ │ ├── ExecutionUsage.php # Token/time usage
│ │ ├── ExecutionModel.php # AI model info
│ │ ├── PlatformInfo.php # Platform data
│ │ ├── SubscriptionInfo.php # Subscription details
│ │ ├── SubscriptionPlan.php # Plan limits/features
│ │ ├── UsageSummary.php # Usage aggregation
│ │ ├── UsageDay.php # Daily usage entry
│ │ └── PaginatedResult.php # Paginated list wrapper
│ ├── Exceptions/
│ │ ├── RefinderException.php # Base exception
│ │ ├── AuthenticationException.php # 401 errors
│ │ ├── SubscriptionException.php # 403 errors
│ │ ├── ValidationException.php # 422 errors
│ │ ├── RateLimitException.php # 429 errors
│ │ ├── ToolException.php # 500/502 errors
│ │ └── ConnectionException.php # Network errors
│ ├── Events/
│ │ ├── ToolExecuted.php # Success event
│ │ ├── ToolExecutionFailed.php # Failure event
│ │ └── RateLimitApproaching.php # Quota warning
│ ├── Contracts/
│ │ └── RefinderClientInterface.php # HTTP client interface
│ └── Testing/
│ ├── RefinderFake.php # Fake manager for tests
│ └── SeoToolFake.php # Fake SEO tool
├── config/
│ └── refinder.php # Configuration file
├── composer.json
├── README.md
├── LICENSE
└── CHANGELOG.md
Authentication
Every request to the Refinder API includes the platform's API key automatically:
X-API-Key: rfnd_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
| Property | Value |
|---|---|
| Prefix | rfnd_ |
| Total length | 53 characters |
| Format | rfnd_ + 48 alphanumeric characters |
The API key is read from config('refinder.api_key') and attached to every HTTP request by the RefinderClient.
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for details.
Security
If you discover a security vulnerability, please send an email to security@refinder.ai instead of using the issue tracker.
License
The MIT License (MIT). Please see LICENSE for more information.