Skip to content

Mewyk/CivitaiSharp

Repository files navigation

CivitaiSharp Logo - Temporary Ai generated image

CivitaiSharp

A modern, lightweight, and AOT-ready .NET 10 client library for all things Civitai.com

Status: Alpha License MIT .NET 10 GitHub NuGet Core

English | Español (Argentina) | 日本語

CivitaiSharp is currently in Alpha: APIs, features, and stability are subject to change.

Table of Contents

  1. Packages and Release Schedule
  2. Installation
  3. Quick Start
  4. Configuration
  5. API Examples
  6. Features
  7. Documentation
  8. Known API Quirks
  9. Versioning
  10. License
  11. Contributing

1. Packages and Release Schedule

Package Status Description
CivitaiSharp.Core Alpha Public API client for models, images, tags, and creators
CivitaiSharp.Sdk Alpha Generator/Orchestration API client for image generation jobs
CivitaiSharp.Tools Alpha Utilities for downloads, file hashing, and HTML parsing

Note: All packages are currently in Alpha. APIs may change between minor versions.

Warning: CivitaiSharp.Sdk is not fully tested and should not be used in production environments. Use at your own risk.

2. Installation

Install via NuGet:

# Core library - API client for models, images, tags, and creators
dotnet add package CivitaiSharp.Core --prerelease

# SDK - Image generation and job management (requires API token)
dotnet add package CivitaiSharp.Sdk --prerelease

# Tools - File hashing, downloads, and HTML parsing
dotnet add package CivitaiSharp.Tools --prerelease

3. Quick Start

Minimal Example

The simplest way to get started with CivitaiSharp.Core:

using CivitaiSharp.Core;
using CivitaiSharp.Core.Extensions;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddCivitaiApi();

await using var provider = services.BuildServiceProvider();
var client = provider.GetRequiredService<IApiClient>();

var result = await client.Models.ExecuteAsync();
if (result.IsSuccess)
{
    foreach (var model in result.Value.Items)
        Console.WriteLine(model.Name);
}

Full Example with Configuration

using CivitaiSharp.Core;
using CivitaiSharp.Core.Extensions;
using CivitaiSharp.Core.Models;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddCivitaiApi(options =>
{
    options.TimeoutSeconds = 120;
    options.StrictJsonParsing = true;
});

await using var provider = services.BuildServiceProvider();
var client = provider.GetRequiredService<IApiClient>();

var result = await client.Models
    .WhereType(ModelType.Checkpoint)
    .WhereNsfw(false)
    .OrderBy(ModelSort.MostDownloaded)
    .ExecuteAsync(resultsLimit: 10);

if (result.IsSuccess)
{
    foreach (var model in result.Value.Items)
        Console.WriteLine($"{model.Name} by {model.Creator?.Username}");
}
else
{
    Console.WriteLine($"Error: {result.ErrorInfo.Message}");
}

4. Configuration

Using appsettings.json

CivitaiSharp.Core reads configuration from the CivitaiApi section by default.

Minimal Configuration (appsettings.json)
{
  "CivitaiApi": {
  }
}

All settings have sensible defaults, so an empty section is valid.

Full Configuration (appsettings.json)
{
  "CivitaiApi": {
    "BaseUrl": "https://civitai.com",
    "ApiVersion": "v1",
    "ApiKey": null,
    "TimeoutSeconds": 30,
    "StrictJsonParsing": false
  }
}
Property Type Default Description
BaseUrl string https://civitai.com Base URL for the Civitai API
ApiVersion string v1 API version path segment
ApiKey string? null Optional API key for authenticated requests
TimeoutSeconds int 30 HTTP request timeout (1-300 seconds)
StrictJsonParsing bool false Throw on unmapped JSON properties

Authentication Note: The Core library can query public endpoints (models, images, tags, creators) without an API key. An API key is only required for authenticated features like favorites, hidden models, and higher rate limits. NSFW Content: Setting WhereNsfw(true) on models or using certain ImageNsfwLevel values (Mature, X) requires authentication. This is different from CivitaiSharp.Sdk which always requires an API token for all operations.

Configuration with IConfiguration
using CivitaiSharp.Core.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

var services = new ServiceCollection();
services.AddCivitaiApi(configuration);
// Or with a custom section name:
// services.AddCivitaiApi(configuration, "MyCustomSection");

await using var provider = services.BuildServiceProvider();

5. API Examples

CivitaiSharp.Core provides fluent builders for each endpoint. Each builder is immutable and thread-safe.

Models Endpoint
// Get all models (default query)
var result = await client.Models.ExecuteAsync();

// Get a specific model by ID
var result = await client.Models.GetByIdAsync(12345);
if (result.IsSuccess)
    Console.WriteLine($"Model: {result.Value.Name}");

// Get the first matching model (efficient single-item retrieval)
var result = await client.Models
    .WhereName("SDXL")
    .FirstOrDefaultAsync();
if (result is { IsSuccess: true, Value: not null })
    Console.WriteLine($"Found: {result.Value.Name}");

// Search by name
var result = await client.Models
    .WhereName("SDXL")
    .ExecuteAsync();

// Filter by type and sort
var result = await client.Models
    .WhereType(ModelType.Checkpoint)
    .OrderBy(ModelSort.MostDownloaded)
    .ExecuteAsync(resultsLimit: 25);

// Filter by tag
var result = await client.Models
    .WhereTag("anime")
    .WhereNsfw(false)
    .ExecuteAsync();

// Filter by creator
var result = await client.Models
    .WhereUsername("Mewyk")
    .OrderBy(ModelSort.Newest)
    .ExecuteAsync();

// Filter by base model (string value, e.g., "SDXL 1.0", "SD 1.5", "Flux.1 D")
var result = await client.Models
    .WhereBaseModel("SDXL 1.0")
    .WhereType(ModelType.Lora)
    .ExecuteAsync();

// Filter by multiple base models
var result = await client.Models
    .WhereBaseModels("SDXL 1.0", "Pony")
    .ExecuteAsync();

// Filter by specific model IDs (ignored if query is also provided)
var result = await client.Models
    .WhereIds(12345, 67890, 11111)
    .ExecuteAsync();

// Get a specific model version by version ID
var versionResult = await client.Models.GetByVersionIdAsync(130072);
if (versionResult.IsSuccess)
{
    Console.WriteLine($"Version: {versionResult.Value.Name}");
    Console.WriteLine($"AIR: {versionResult.Value.AirIdentifier}");
}

// Get a model version by file hash (SHA256, AutoV2, CRC32, etc.)
var hashResult = await client.Models.GetByVersionHashAsync("ABC123DEF456");
if (hashResult.IsSuccess)
{
    Console.WriteLine($"Found: {hashResult.Value.Model?.Name}");
    Console.WriteLine($"AIR: {hashResult.Value.AirIdentifier}");
}
Images Endpoint
// Get all images (default query)
var result = await client.Images.ExecuteAsync();

// Get the first matching image
var result = await client.Images
    .WhereModelId(12345)
    .FirstOrDefaultAsync();
if (result is { IsSuccess: true, Value: not null })
    Console.WriteLine($"Image URL: {result.Value.Url}");

// Filter by model ID
var result = await client.Images
    .WhereModelId(12345)
    .ExecuteAsync();

// Filter by model version ID
var result = await client.Images
    .WhereModelVersionId(67890)
    .OrderBy(ImageSort.Newest)
    .ExecuteAsync();

// Filter by username
var result = await client.Images
    .WhereUsername("Mewyk")
    .WhereNsfwLevel(ImageNsfwLevel.None)
    .ExecuteAsync(resultsLimit: 50);

// Filter by post ID
var result = await client.Images
    .WherePostId(11111)
    .ExecuteAsync();
Tags Endpoint
// Get all tags (default query)
var result = await client.Tags.ExecuteAsync();

// Search tags by name
var result = await client.Tags
    .WhereName("portrait")
    .ExecuteAsync(resultsLimit: 100);
Creators Endpoint
// Get all creators (default query)
var result = await client.Creators.ExecuteAsync();

// Search creators by name
var result = await client.Creators
    .WhereName("Mewyk")
    .ExecuteAsync(resultsLimit: 20);

// Page-based pagination (Models, Tags, and Creators use pages, not cursors)
var result = await client.Creators
    .WithPageIndex(2)
    .ExecuteAsync(resultsLimit: 50);
Pagination
// Cursor-based pagination (Images only)
string? cursor = null;
var allImages = new List<Image>();

do
{
    result = await client.Images
        .WhereModelId(12345)
        .ExecuteAsync(resultsLimit: 100, cursor: cursor);

    if (!result.IsSuccess)
        break;

    allImages.AddRange(result.Value.Items);
    cursor = result.Value.Metadata?.NextCursor;
    
} while (cursor is not null);

// Page-based pagination (Models, Tags, Creators)
var page1 = await client.Models.WithPageIndex(1).ExecuteAsync();
var page2 = await client.Tags.WithPageIndex(2).ExecuteAsync();
var page3 = await client.Creators.WithPageIndex(3).ExecuteAsync();
Tools - File Hashing
using CivitaiSharp.Tools.Hashing;
using CivitaiSharp.Tools.Extensions;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddCivitaiDownloads();

await using var provider = services.BuildServiceProvider();
var hashingService = provider.GetRequiredService<IFileHashingService>();

// Compute SHA256 hash
var result = await hashingService.ComputeHashAsync(
    @"C:\Models\model.safetensors",
    HashAlgorithm.Sha256);

if (result.IsSuccess)
{
    Console.WriteLine($"Hash: {result.Value.Hash}");
    Console.WriteLine($"Size: {result.Value.FileSize:N0} bytes");
}

// Supported algorithms: Sha256, Sha512, Blake3, Crc32
var blake3Result = await hashingService.ComputeHashAsync(filePath, HashAlgorithm.Blake3);
Tools - Downloading Files
using CivitaiSharp.Core;
using CivitaiSharp.Core.Extensions;
using CivitaiSharp.Tools.Downloads;
using CivitaiSharp.Tools.Extensions;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddCivitaiApi();
services.AddCivitaiDownloads(options =>
{
    options.Images.BaseDirectory = @"C:\Downloads\Images";
    options.Images.PathPattern = "{Username}/{Id}.{Extension}";
    
    options.Models.BaseDirectory = @"C:\Models";
    options.Models.PathPattern = "{ModelType}/{ModelName}/{FileName}";
    options.Models.VerifyHash = true;
});

await using var provider = services.BuildServiceProvider();
var apiClient = provider.GetRequiredService<IApiClient>();
var downloadService = provider.GetRequiredService<IDownloadService>();

// Download a model file with hash verification
var modelResult = await apiClient.Models.GetByIdAsync(4201);
if (modelResult.IsSuccess)
{
    var version = modelResult.Value.ModelVersions?.FirstOrDefault();
    var file = version?.Files?.FirstOrDefault(f => f.Primary == true);
    
    if (file is not null && version is not null)
    {
        var downloadResult = await downloadService.DownloadAsync(file, version);
        if (downloadResult.IsSuccess)
        {
            Console.WriteLine($"Downloaded: {downloadResult.Value.FilePath}");
            Console.WriteLine($"Verified: {downloadResult.Value.IsVerified}");
        }
    }
}
Tools - HTML Parsing
using CivitaiSharp.Tools.Parsing;

// Convert HTML description to Markdown
var markdown = HtmlParser.ToMarkdown(model.Description);

// Convert to plain text
var plainText = HtmlParser.ToPlainText(model.Description);

// Or use extension methods on Model/ModelVersion
var markdown = model.GetDescriptionAsMarkdown();
var plainText = modelVersion.GetDescriptionAsPlainText();
SDK - Image Generation (Jobs)
using CivitaiSharp.Sdk;
using CivitaiSharp.Sdk.Extensions;
using CivitaiSharp.Sdk.Air;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddCivitaiSdk(options =>
{
    options.ApiToken = "your-api-token"; // Required for SDK
});

await using var provider = services.BuildServiceProvider();
var sdkClient = provider.GetRequiredService<ISdkClient>();

// Create a text-to-image job
var model = AirIdentifier.Parse("urn:air:sdxl:checkpoint:civitai:4201@130072");

var result = await sdkClient.Jobs
    .CreateTextToImage()
    .WithModel(model)
    .WithPrompt("a beautiful sunset over mountains, highly detailed")
    .WithNegativePrompt("blurry, low quality")
    .WithSize(1024, 1024)
    .WithSteps(30)
    .WithCfgScale(7.5m)
    .WithSeed(12345)
    .ExecuteAsync();

if (result is Result<JobStatusCollection>.Success success)
{
    var token = success.Data.Token;
    Console.WriteLine($"Job submitted: {token}");
    
    // Query job status
    var statusResult = await sdkClient.Jobs.Query
        .WithDetailed()
        .GetByTokenAsync(token);
    
    if (statusResult is Result<JobStatusCollection>.Success statusSuccess)
    {
        foreach (var job in statusSuccess.Data.Jobs)
        {
            Console.WriteLine($"Job {job.JobId}: {job.Status}");
        }
    }
}

// Wait for job completion (blocks up to ~10 minutes)
var completedResult = await sdkClient.Jobs.Query
    .WithWait()
    .WithDetailed()
    .GetByTokenAsync(token);

// Query jobs by custom properties
var queryResult = await sdkClient.Jobs.Query
    .WhereProperty("userId", JsonSerializer.SerializeToElement("12345"))
    .WhereProperty("environment", JsonSerializer.SerializeToElement("production"))
    .ExecuteAsync();
SDK - Coverage Service
using CivitaiSharp.Sdk;
using CivitaiSharp.Sdk.Air;

// Check if a model is available before submitting a job
var model = AirIdentifier.Parse("urn:air:sdxl:checkpoint:civitai:4201@130072");
var lora = AirIdentifier.Parse("urn:air:sdxl:lora:civitai:328553@368189");

// Check single model
var coverageResult = await sdkClient.Coverage.GetAsync(model);

if (coverageResult is Result<ProviderAssetAvailability>.Success coverage)
{
    if (coverage.Data.Available)
    {
        Console.WriteLine("Model is available!");
        foreach (var (provider, status) in coverage.Data.Providers)
        {
            Console.WriteLine($"  {provider}: Queue position {status.QueuePosition}");
        }
    }
    else
    {
        Console.WriteLine("Model not available on any provider");
    }
}

// Check multiple resources at once
var resources = new[] { model, lora };
var batchResult = await sdkClient.Coverage.GetAsync(resources);

if (batchResult is Result<IReadOnlyDictionary<AirIdentifier, ProviderAssetAvailability>>.Success batch)
{
    foreach (var (resource, availability) in batch.Data)
    {
        Console.WriteLine($"{resource}: {availability.Available}");
    }
}
SDK - Usage Service
using CivitaiSharp.Sdk;

// Get current account usage
var usageResult = await sdkClient.Usage.GetConsumptionAsync();

if (usageResult is Result<ConsumptionDetails>.Success usage)
{
    Console.WriteLine($"Total Jobs: {usage.Data.TotalJobs}");
    Console.WriteLine($"Total Credits: {usage.Data.TotalCredits:F2}");
    Console.WriteLine($"Period: {usage.Data.StartDate} to {usage.Data.EndDate}");
}

// Get usage for specific date range
var startDate = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var endDate = new DateTime(2025, 1, 31, 23, 59, 59, DateTimeKind.Utc);

var monthlyResult = await sdkClient.Usage.GetConsumptionAsync(startDate, endDate);

if (monthlyResult is Result<ConsumptionDetails>.Success monthly)
{
    Console.WriteLine($"January 2025:");
    Console.WriteLine($"  Jobs: {monthly.Data.TotalJobs}");
    Console.WriteLine($"  Credits: {monthly.Data.TotalCredits:F2}");
    
    if (monthly.Data.JobsByType is not null)
    {
        Console.WriteLine("  Breakdown by type:");
        foreach (var (jobType, count) in monthly.Data.JobsByType)
        {
            var credits = monthly.Data.CreditsByType?.GetValueOrDefault(jobType, 0) ?? 0;
            Console.WriteLine($"    {jobType}: {count} jobs, {credits:F2} credits");
        }
    }
}
Error Handling
var result = await client.Models.ExecuteAsync();

// Pattern matching
var message = result switch
{
    { IsSuccess: true, Value: var page } => $"Found {page.Items.Count} models",
    { IsSuccess: false, ErrorInfo: var error } => error.Code switch
    {
        ErrorCode.RateLimited => "Too many requests, please slow down",
        ErrorCode.Unauthorized => "Invalid or missing API key",
        ErrorCode.NotFound => "Resource not found",
        _ => $"Error: {error.Code} - {error.Message}"
    }
};

// Traditional approach
if (result.IsSuccess)
{
    foreach (var model in result.Value.Items)
        Console.WriteLine(model.Name);
}
else
{
    Console.WriteLine($"Failed: {result.ErrorInfo.Message}");
}

6. Features

  • Modern .NET 10 - Built with nullable reference types, records, and primary constructors
  • AOT-Ready - Full Native AOT and trimming support with source-generated JSON serialization
  • Fluent Query Builders - Type-safe, immutable, thread-safe builders for constructing API requests
  • Result Pattern - Explicit success/failure handling without exceptions for expected errors
  • Built-in Resilience - Standard resilience handler with retry policies, circuit breakers, rate limiting, and timeouts
  • Dependency Injection - First-class support for IHttpClientFactory and Microsoft DI

AOT and Trimming Support

All CivitaiSharp packages are fully compatible with Native AOT compilation and IL trimming:

  • Source-Generated JSON - Uses System.Text.Json source generators (JsonSerializerContext) for reflection-free serialization
  • Trim-Safe - All packages have <IsAotCompatible>true</IsAotCompatible> and <EnableTrimAnalyzer>true</EnableTrimAnalyzer>
  • No Runtime Reflection - All type metadata is generated at compile-time

To publish with AOT:

dotnet publish -c Release -r win-x64 /p:PublishAot=true

To publish with trimming:

dotnet publish -c Release -r win-x64 /p:PublishTrimmed=true

7. Documentation

8. Known API Quirks

CivitaiSharp interacts with the Civitai.com API, which has several known quirks. Some are mitigated automatically; others are documented and under investigation.

Issue Description
Tags endpoint model count Documented feature, but responses never include this field
limit=0 parameter Documented to return all results for various endpoints, but returns an error
Semantic errors with HTTP 200 Errors, Not Found, and others, all return HTTP 200
Endpoint inconsistencies Intermittent throttling, unreported outages, undocumented rate limits
Variable metadata structures Metadata format varies widely between responses
User existence issues During partial outages, existing users may appear nonexistent
Creator endpoint unreliability See details below

Creator Endpoint Reliability Issues

The /api/v1/creators endpoint is known to experience intermittent reliability issues:

  • HTTP 500 errors: The endpoint frequently returns server errors, especially under load
  • Slow response times: Requests may take significantly longer than other endpoints (10-30+ seconds)
  • Timeout failures: Long response times can exceed client timeout thresholds

Recommendations:

  1. Implement generous timeouts: Set timeouts of 60-120 seconds for Creator endpoint requests
  2. Use retry logic: The built-in resilience handler will retry on 500 errors, but success is not guaranteed
  3. Handle failures gracefully: Your application should degrade gracefully when Creator data is unavailable
  4. Cache results aggressively: When requests succeed, cache the results to reduce API load
// Example: Handling Creator endpoint unreliability
var result = await client.Creators.ExecuteAsync(resultsLimit: 10);

if (!result.IsSuccess)
{
    // Log the error but continue with degraded functionality
    logger.LogWarning("Creator data unavailable: {Error}", result.ErrorInfo.Message);
    return GetCachedCreators(); // Fallback to cached data
}

Additional quirks are being tracked and will be addressed in future releases.

9. Versioning

CivitaiSharp follows MAJOR.MINOR.PATCH semantic versioning:

Component Description
MAJOR Significant, breaking API changes
MINOR New features; may include limited breaking changes unlikely to affect most users
PATCH Backwards-compatible bug fixes and improvements

Pre-release versions use the format: MAJOR.MINOR.PATCH-alpha.N

Note: While in Alpha (0.x.x), APIs may change between minor versions. Stability is guaranteed from v1.0.0 onwards.

10. License

This repository is released under the MIT License.

11. Contributing

Contributions are welcome. Please read CONTRIBUTING.md for guidelines.

Legal Notice

CivitaiSharp is an independent open-source project and is not affiliated with, sponsored by, endorsed by, or officially associated with Civitai.com or Civitai, Inc. The Civitai name and any related trademarks are the property of their respective owners. Use of these names is for identification purposes only and does not imply any endorsement or partnership.


GitHub | Documentation | Civitai

About

A modern, lightweight, and AOT-ready .NET 10 client library for all things Civitai.com

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published