diff --git a/filter_api/filter_api.api.php b/filter_api/filter_api.api.php
new file mode 100644
index 0000000..043c864
--- /dev/null
+++ b/filter_api/filter_api.api.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the Filter API module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+ /**
+  * Declare variables used by filters.
+  *
+  * @return
+  *  An array keyed by filter delta where each item is itself an array of
+  *  settings definitions. Each definition is of the form:
+  *    variable_base_name => description
+  *  where the variable base name is the variable name without the filter ID
+  *  appended.
+  */
+function hook_filter_api_variable_info() {
+  return array(
+    0 => array(
+      'filter_html' => 'Filter HTML tags',
+      'allowed_html' => 'Allowed HTML tags',
+      'filter_html_help' => 'Display HTML help',
+      'filter_html_nofollow' => 'Spam link deterrent',
+    ),
+    1 => array(),
+    2 => array(
+      'filter_url_length' => 'Maximum link text length',
+    ),
+    3 => array(),
+  );
+}
diff --git a/filter_api/filter_api.info b/filter_api/filter_api.info
new file mode 100644
index 0000000..8dcda86
--- /dev/null
+++ b/filter_api/filter_api.info
@@ -0,0 +1,3 @@
+name = Filter API
+description = Provides an API to core's Filter module.
+core = 6.x
diff --git a/filter_api/filter_api.module b/filter_api/filter_api.module
new file mode 100644
index 0000000..c87d14e
--- /dev/null
+++ b/filter_api/filter_api.module
@@ -0,0 +1,230 @@
+<?php
+/**
+ * @file filter_api.module
+ * Provides an API for working with input formats and filters.
+ */
+
+/**
+ * Retrieves all input formats currently defined.
+ *
+ * This differs from core's filter_formats() because that is based on returning
+ * only the formats the current user has access to.
+ *
+ * @param $format_id
+ *  (optional) The numerical id of a single format to return. If not given, all
+ *  formats are returned.
+ *
+ * @return
+ *  An array of format objects, keyed by format id. Each item has properties:
+ *  - 'format': The format id.
+ *  - 'name': The human-readable label.
+ *  - 'roles': A badly-formatted string of role ids.
+ *  - 'roles_array': An array of roles (role id => role name) that may use this
+ *    format.
+ *  - 'cache': The cache setting for the format.
+ *  - 'filters': An array of filter objects for this format, as returned by
+ *    filter_list_format(). This is keyed by the string 'module/delta', and in
+ *    order of weight. Each filter object has a 'settings' property added, which
+ *    contains settings as variable_base_name => value.
+ *  - 'settings': An array of the same settings as in 'filters', grouped by
+ *    module only.
+ */
+function filter_api_filter_formats($format_id = NULL) {
+  static $formats;
+  if (!isset($formats)) {
+    // Load the basic format data from the database. This gives us an object with
+    // keys format, name, roles, cache.
+    $query = 'SELECT * FROM {filter_formats}';
+    $result = db_query($query, $args);
+    while ($format = db_fetch_object($result)) {
+      $formats[$format->format] = $format;
+    }
+
+    // Add a clean array of roles.
+    $user_roles = user_roles();
+    foreach ($formats as $id => $format) {
+      $format->roles_array = array();
+      // TODO: the role names are translated, this is apparently a bad thing.
+      foreach ($user_roles as $rid => $name) {
+        // Fill a roles array with roles that may access the filter.
+        if (strstr($format->roles, ",$rid,")) {
+          $format->roles_array[$rid] = $name;
+        }
+      }
+      // Add the list of filters.
+      $format->filters = filter_list_format($id);
+
+      // Load the settings into the filters.
+      filter_api_format_settings_load($format);
+    }
+
+    // Mark the default format.
+    $default_format_id = variable_get('filter_default_format', 1);
+    $formats[$default_format_id]->default = TRUE;
+  }
+
+  if (isset($format_id)) {
+    return $formats[$format_id];
+  }
+  else {
+    return $formats;
+  }
+}
+
+/**
+ * Load the settings for a given input format.
+ *
+ * @param $input_format
+ *   An input format object. Settings are loaded into its filter property.
+ */
+function filter_api_format_settings_load(&$input_format) {
+  // Get the defined list of variable names for all filters.
+  // Use static, as we come here several times.
+  static $filter_variables_info;
+  if (!isset($filter_variables_info)) {
+    $filter_variables_info = filter_api_get_filter_variables();    
+  }
+
+  // Get the variables for this format's filters
+  $input_format->settings = array();
+  $format_filters = $input_format->filters;
+  foreach ($format_filters as $filter_key => $filter) {
+    // Initialize an empty settings array in the filter object.
+    $input_format->filters[$filter_key]->settings = array();
+
+    $variables = array_keys($filter_variables_info[$filter->module][$filter->delta]);
+    foreach ($variables as $variable_base_name) {
+      // Construct the real variable name by appending the format id.
+      $variable_filter_name = $variable_base_name . '_' . $input_format->format;
+      // If a variable value is not defined, don't add it here, the owner
+      // module will provide a valid value by default, and here we
+      // cannot determine which is the correct default value.
+      $value = variable_get($variable_filter_name, NULL);
+      if (isset($value)) {
+        $input_format->settings[$filter->module][$variable_base_name] = $value;
+        $input_format->filters[$filter_key]->settings[$variable_base_name] = $value;
+      }
+    }
+  }
+
+  // Allow other modules to define custom variables
+  foreach (module_implements('input_formats_variables') as $module) {
+    // TODO: fix this, as $filter is empty right now.
+    foreach ($filter as $delta) {
+      $extra = module_invoke($module, 'input_formats_variables', $delta);
+      foreach ($extra as $variable_name => $title) {
+        $value = variable_get($variable_name . '_' . $input_format->format, NULL);
+        if (isset($value)) {
+          $input_format->settings[$module][$variable_name] = array(
+            '#description' => $title,
+            '#value' => $value,
+          );
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Get all variable names used by filter modules as filter settings.
+ *
+ * This requires some jumping through hoops and form parsing (!) because each
+ * format filter stores the settings in whichever way it likes, usually in the
+ * {variable} table.
+ *
+ * @return
+ *  An array of variable base names, ie without the final format id.
+ *  This is keyed by providing module, then filter delta.
+ */
+function filter_api_get_filter_variables() {
+  // We can't use module_invoke_all() because we need data keyed by module.
+  $settings = array();
+  foreach (module_implements('filter_api_variable_info') as $module) {
+    $settings[$module] = module_invoke($module, 'filter_api_variable_info');
+  }
+
+  // Add in settings for modules that don't implement this hook.
+  $settings = array_merge($settings, _filter_api_filter_api_variable_info());
+  return $settings;
+}
+
+/**
+ * Implementation of hook_filter_api_variable_info() on behalf of filter module.
+ *
+ * @return
+ *  An array keyed by filter delta where each item is an array of variables in
+ *  form variable_name => description.
+ */
+function filter_filter_api_variable_info() {
+  return array(
+    0 => array(
+      'filter_html' => 'Filter HTML tags',
+      'allowed_html' => 'Allowed HTML tags',
+      'filter_html_help' => 'Display HTML help',
+      'filter_html_nofollow' => 'Spam link deterrent',
+    ),
+    1 => array(),
+    2 => array(
+      'filter_url_length' => 'Maximum link text length',
+    ),
+    3 => array(),
+  );
+}
+
+/**
+ * Provide hook_filter_api_filter_variables() data for other modules.
+ *
+ * Detects filter settings data from the filter settings form for all filter
+ * modules that do not implement hook_filter_api_filter_variables().
+ * This can't be a hook implementation because it puts module keys into the
+ * data it returns.
+ */
+function _filter_api_filter_api_variable_info() {
+  $filter_variables = array();
+  foreach (module_implements('filter') as $module) {
+    if (module_hook($module, 'filter_api_variable_info')) {
+      continue;
+    }
+
+    $module_filters = module_invoke($module, 'filter', 'list');
+    $filter_variables[$module] = array();
+    foreach ($module_filters as $delta => $label) {
+      $filter_variables[$module][$delta] = array();
+      $filter_form = module_invoke($module, 'filter', 'settings', $delta);
+      if (isset($filter_form) && is_array($filter_form)) {
+        $module_variables = array();
+        filter_api_walk_form($filter_form, $module_variables);
+
+        // Because we invoke the settings form with no filter, some modules
+        // put a -1 at the end of the key.
+        foreach ($module_variables as $key => $description) {
+          if (substr($key, -3) == '_-1') {
+            $trimmed_key = substr($key, 0, -3);
+            $module_variables[$trimmed_key] = $description;
+            unset($module_variables[$key]);
+          }
+        }
+
+        $filter_variables[$module][$delta] = $module_variables;
+      }
+    }
+  }
+  return $filter_variables;
+}
+
+/**
+ * Walk each form items searching for non fieldsets widgets.
+ *
+ * This widgets contains the names of the variables that the input
+ * format uses.
+ */
+function filter_api_walk_form($form, &$variables) {
+  foreach (element_children($form) as $key) {
+    if ($form[$key]['#type'] == 'fieldset') {
+      filter_api_walk_form($form[$key], $variables);
+    }
+    else {
+      $variables[$key] = $form[$key]['#title'];
+    }
+  }
+}
