diff --git plus1.api.php plus1.api.php
new file mode 100644
index 0000000..dfc372c
--- /dev/null
+++ plus1.api.php
@@ -0,0 +1,49 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Hooks provided by the Plus1 module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Control whether voting is allowed.
+ *
+ * Modules may implement this hook if they want to have a say in whether or not
+ * a given user is allowed to vote on a given node.
+ *
+ * The administrative account (user ID #1) does not bypass this access check.
+ *
+ * Note that not all modules will want to influence access. If your module
+ * does not want to actively grant or block access, return PLUS1_ACCESS_IGNORE
+ * or simply return nothing. Blindly returning FALSE will break other Plus1
+ * access modules.
+ *
+ * @param $nid
+ *   The node id on which the vote is to be cast.
+ * @param $op
+ *   The operation to be performed. Possible values:
+ *   - "create"
+ *   - "view"
+ * @param $account
+ *   A user object representing the user for who is about to cast the vote.
+ *
+ * @return
+ *   PLUS1_ACCESS_ALLOW if voting is to be allowed;
+ *   PLUS1_ACCESS_DENY if voting is to be denied;
+ *   PLUS1_ACCESS_IGNORE to not affect voting at all.
+ */
+function hook_plus1_access($node, $op, $account) {
+  if (!in_array($node->type, variable_get('plus1_nodetypes', array('story')))) {
+    return PLUS1_ACCESS_DENY;
+  }
+  if ($op == 'vote' && plus1_get_votes($node->nid, $account->uid)) {
+    return PLUS1_ACCESS_DENY;
+  }
+  return PLUS1_ACCESS_IGNORE;
+}
\ No newline at end of file
diff --git plus1.module plus1.module
index cc9749e..5eb8d6e 100644
--- plus1.module
+++ plus1.module
@@ -6,6 +6,21 @@
 */
 
 /**
+ * Modules should return this value from hook_plus1_access() to allow access to voting on a node.
+ */
+define('PLUS1_ACCESS_ALLOW', 'allow');
+
+/**
+ * Modules should return this value from hook_plus1_access() to deny access to voting on a node.
+ */
+define('PLUS1_ACCESS_DENY', 'deny');
+
+/**
+ * Modules should return this value from hook_plus1_access() to not affect access to voting on a node.
+ */
+define('PLUS1_ACCESS_IGNORE', NULL);
+
+/**
 * Implementation of hook_perm().
 */
 function plus1_perm() {
@@ -124,9 +139,9 @@ function plus1_vote($nid) {
     return drupal_access_denied();
   }
 
-  $voted = plus1_get_votes($nid, $user->uid);
-  // If the voter has not already voted.
-  if (!$voted) {
+  $node = node_load($nid);
+
+  if (plus1_vote_access('create', $node, $user)) {
     $votes[] = array(
       'content_id' => $nid,
       'value_type' => 'points',
@@ -220,8 +235,7 @@ function plus1_jquery_widget($node, $teaser, $page) {
 function plus1_nodeapi(&$node, $op, $teaser, $page) {
   switch ($op) {
     case 'view':
-      // Only show the voting widget in allowed content types.
-      if (in_array($node->type, variable_get('plus1_nodetypes', array('story')))) {
+      if (plus1_vote_access('view', $node)) {
         // Show the widget.
         if (($teaser && variable_get('plus1_in_teaser', 0)) || (!$teaser && variable_get('plus1_in_full_view', 1))) {
           $node->content['plus1_widget'] = array(
@@ -239,6 +253,50 @@ function plus1_nodeapi(&$node, $op, $teaser, $page) {
   }
 }
 
+function plus1_vote_access($op, $node, $account = NULL) {
+  global $user;
+
+  if (!$node || !in_array($op, array('view', 'create'), TRUE)) {
+    // If there was no node to check against, or the $op was not one of the
+    // supported ones, we return access denied.
+    return FALSE;
+  }
+  // If no user object is supplied, the access check is for the current user.
+  if (empty($account)) {
+    $account = $user;
+  }
+
+  $vote_allow = TRUE;
+
+  foreach (module_implements('plus1_access') as $module) {
+    $status = module_invoke($module, 'plus1_access', $node, 'view', $user);
+    if ($status === PLUS1_ACCESS_ALLOW) {
+      break;
+    }
+    elseif ($status === PLUS1_ACCESS_DENY) {
+      $vote_allow = FALSE;
+      break;
+    }
+  }
+
+  return $vote_allow;
+}
+
+/**
+ * Implementation of hook_plus1_access().
+ */
+function plus1_plus1_access($node, $op, $account) {
+  // Only show widget on selected node types
+  if (!in_array($node->type, variable_get('plus1_nodetypes', array('story')))) {
+    return PLUS1_ACCESS_DENY;
+  }
+  // If the user has already voted - don't let another vote be registered
+  if ($op == 'vote' && plus1_get_votes($node->nid, $account->uid)) {
+    return PLUS1_ACCESS_DENY;
+  }
+  return PLUS1_ACCESS_IGNORE;
+}
+
 /**
 * Implementation of hook_theme().
 */
