aitoolsblocklist for Dart and Flutter

Ask one question about any hostname: is this an AI tool, and if so, what kind? This package answers it from Dart code by calling the lookup endpoint of an anti-AI blocklist API. It works the same in a Flutter app, a command-line script, or a server written with shelf or dart_frog.

The register behind the endpoint covers more than 20,000 classified AI tool domains. A lookup returns the category of the tool, the type of AI it offers, and what the vendor's terms say about training on customer data.

Install

dart pub add aitoolsblocklist

For Flutter projects, flutter pub add aitoolsblocklist does the same thing. The only runtime dependency is package:http.

A first lookup

import 'dart:io';
import 'package:aitoolsblocklist/aitoolsblocklist.dart';

Future<void> main() async {
  final client = AIToolsBlocklistClient(
    apiKey: Platform.environment['AQ_API_KEY'] ?? '',
  );
  try {
    final result = await client.check('chat.openai.com');
    if (result['blocked'] == true) {
      print('${result['domain']} is an AI tool: ${result['primary_category']}');
    } else {
      print('${result['domain']} is not in the register');
    }
  } finally {
    client.close();
  }
}

check accepts a bare domain or a subdomain. If you pass eu.app.example-ai.com and only example-ai.com is listed, the response tells you which parent matched.

What comes back

The client returns an ApiResult. Index it like a map, or call toJson() for an unmodifiable copy. For a listed domain the fields are:

Field Meaning
domain The value you sent, normalised
blocked true when the domain belongs to a known AI tool
matched_domain Present only when a parent domain matched instead of the exact name
primary_category The main job of the tool, for example writing, image generation or coding
ai_type The kind of AI the tool offers
categories A list of {category, subcategory} pairs for tools that do several things
trains_on_data What the vendor terms say about training on your inputs, or unstated
opt_out_available Whether the terms describe an opt-out
enterprise_no_training Whether a business plan excludes training
api_no_training Whether API traffic is excluded from training
terms_checked The date those terms were last reviewed

For a domain that is not listed you get blocked: false and an empty categories list. Treat that as "not a known AI tool", not as "safe".

The word unstated matters. It means the terms were read and say nothing on the point. That is a finding in its own right, and many policies treat it the same as "yes".

Deciding what to do with a result

The register tells you what a tool is. Your policy decides what happens next. A small mapping keeps that decision in one place:

enum Action { allow, warn, block }

Action decide(ApiResult r) {
  if (r['blocked'] != true) return Action.allow;
  final trains = r['trains_on_data'];
  if (trains == 'yes' || trains == 'unstated') return Action.block;
  return Action.warn;
}

Teams usually start with a rule like this and then add exceptions for tools they have approved, keyed on matched_domain ?? domain.

Using it inside a Flutter app

A browser-style app, a kiosk or a managed student device can check a link before opening it. Keep the key off the device if you can. The simplest safe pattern is to route lookups through your own backend and pass that backend's URL as baseUrl:

final client = AIToolsBlocklistClient(
  apiKey: sessionToken,
  baseUrl: 'https://api.your-company.example/ai-lookup',
);

Your backend then adds the real key and forwards the call. The client appends /check to whatever base you give it.

Using it on a Dart server

On a proxy or gateway written in Dart, cache results, because the same few hundred domains make up most traffic. A plain map with a timestamp is enough for a single process:

final _cache = <String, (DateTime, ApiResult)>{};

Future<ApiResult> cachedCheck(AIToolsBlocklistClient c, String host) async {
  final hit = _cache[host];
  if (hit != null && DateTime.now().difference(hit.$1).inHours < 24) {
    return hit.$2;
  }
  final fresh = await c.check(host);
  _cache[host] = (DateTime.now(), fresh);
  return fresh;
}

A 24 hour lifetime matches how often the register changes for most domains.

Timeouts and your own HTTP client

The constructor takes an optional timeout (30 seconds by default) and an optional http.Client. Passing your own client lets you share connection pools, add logging, or plug in a retry wrapper from another package. Call close() when you are done, unless you passed a client you close yourself.

Errors

Exception When it is thrown
ArgumentError Empty API key or empty domain, before any network call
AuthenticationException HTTP 401 or 403: a missing or wrong key, or the monthly quota is used up
RateLimitException HTTP 429: too many requests in a short time
ApiException Any other HTTP error, or a response that is not a JSON object

All three custom types extend ApiException, so one on ApiException catch (e) covers them. Each carries statusCode and the raw body for logging. The client does not retry by itself. If you want retries, back off on RateLimitException and leave AuthenticationException alone, since a retry cannot fix a bad key.

Testing without the network

package:http ships a MockClient, which makes unit tests straightforward:

import 'package:http/http.dart' as http;
import 'package:http/testing.dart';

final fake = MockClient((req) async => http.Response(
      '{"domain":"example.com","blocked":false,"categories":[]}', 200));
final client = AIToolsBlocklistClient(apiKey: 'test', httpClient: fake);

Getting a key

Pick a key on the AI Tools Blocklist pricing page. The lookup API is metered per call. The downloadable database suits resolvers and firewalls that need every domain locally.

Where this fits

A lookup answers questions one domain at a time. Two neighbouring services help when the question is bigger:

Other clients for the same register

The same lookup is available outside Dart:

Source for this package lives in the repository linked from the pub.dev sidebar. Issues and pull requests are welcome there.

License

MIT. See LICENSE.

Libraries

aitoolsblocklist