Bulk deleting 30k scam accounts

I’ve recently noticed that ~80% of my 40k Shopify customer accounts seem to be spam and all follow the same format where the first name is their last name also. For example: Josephagomo Josephagomo, maryellenlb16 maryellenlb16. I don’t know where these came from as they are created separately over the course of several years, and Shopify doesn’t list a source for any of them. Regardless, I’d like to delete them, but I can’t seem to run a flow on existing customers, only new customers. I would love help here. Thanks!

Hi @yonik. For your problem, you need to filter customers with first name and last name, and no orders. To delete those customers, you can do the following:

  • Access to Shopify Flow.
  • Set Scheduled time trigger.
  • Add Get customer data:
    • Set Maximum number of customers: 100.
    • Customers with no orders (Get all customers who have created 0 orders).
  • Add For each loop: Get customer data.
  • Add Condition: First name equal to Last name.
  • Add Action: Delete customer.

You cannot delete a customer if they have a past order, Shopify blocks that due to order-history retention.

In addition, Shopify has official docs to guide you on how to Delete customers in many different ways that you can refer to if you cannot apply the method I suggested: Delete customer.

Hope this helps :blush:

Hi Howie,
Thank you so much for this detailed response! I really appreciate it! I ran it as you said, and it seemed to run it once, and not properly iterate. Any ideas of how to fix this?
Thanks!
Yoni K

Thanks Howie!
I just edited it, and it actually deleted the problematic accounts! The only issue now is that it can only pull 100 accounts every 10 minutes, so it will take a long time for it to clean up all of the accounts. Additionally, I am worried that it might review the same accounts over and over again until the first 100 accounts it pulls each time are all valid, and then the flow becomes useless. How do i get it to only look at accounts it hasn’t previously looked at before?

Yep, the usual way is to look at the customer and assign a tag, say “processed”.
The initial “Get customer data” should be modified to skip tagged customers, like this:
orders_count:0 AND tag_not:processed

Unfortunately, i think you can’t query for customers which has the same first and last name. The list of parameters, just in case, is here, under querycustomers - GraphQL Admin

Another useful workflow is periodically deleting all customer tags from all customers, so customer data isn’t cluttered. Especially if you create lots of unnecessary tags.

:bomb: :bomb: :bomb: Be SUPER careful here you applying a broad assumption to EVERY account. :bomb: :bomb: :bomb:

John John, Lauren Lauren, etc are real names that happen.

You really need some other metric DO NOT apply a naive first == last therefore nuke.
Or have had prequalified the entire list by doing something like manually tagging them in a spreadsheet THEN delete based on the tag as a verification method.
→ or order total value ←
Having a tag also means subsequent queries become smaller and smaller as illegitimate accounts are deleted.

e.g. Falsehoods Programmers Believe About Names | Kalzumeus Software.
#19. People’s first names and last names are, by necessity, different.
First middle and last can all be the same either legally or that’s what the customer wants.
Erroneously deleting customers can be a financial or legal RISK.
Think it through before pulling the trigger.

You might want to use a more featureful automation tool.
Mechanic has a trial, or a pay what feels good pricing
The library has a delete task when new orderless accounts are created .
That could be modified to be manually triggerable to send accounts in bulk to it.
Actually I’ll dig around and see if that exists somewhere.

There is no risk in deleting any customer beyond potential future revenue. The only customers you can delete are ones whom you have no legal obligation in the first place. E.g. signed up to email, abandoned checkout, etc. Shopify prevents customers who have made an order from being deleted.

A lack of an order is not a measure of obligation, nor are shopify’s safe gaurds the same as a business obligations nor all encompassing in protecting such things.
B2B may have accounts that don’t place orders but are part of agreements.
Importing existing customers without orders associated yet.
A customer can be unlinked from an order for various reasons , etc etc etc etc.
Approach such assumptions with caution where several others peoples money is involved.

Sure, if you got some contracts or something like that with someone, and they don’t make official orders. I can dig it. I’ll amend. Just be aware of your situation before doing so.

Too right

@yonik if flow isn’t getting it done here’s a sample script for mechanic hodgepodged from a couple different tasks.

:foot: :water_pistol: :collision: Testing and fixes left to the reader, I only made this because I noticed there’s not a good BULK starting point as normally you can ony send 50 resources at a time to mechanic|apps from the shopify admin.
Please look for foot guns as I have not tested the logic end to end and this isn’t doing any other checks besides customer.firstName == customer.lastName but it does have boilerplate for using tags as speedbumbs.

Subscriptions - Mechanic App - Nuke Customer Accounts - Bulk Query Operation
subs:
mechanic/user/trigger
mechanic/shopify/bulk_operation

:technologist: This queries in bulk then uses REST delete

Script - Mechanic App - Nuke Customer Accounts - Bulk Query Operation
{% comment %} 2025 PaulN - this is as is , review and think ahead before using{% endcomment %}
{% comment %}subs:
mechanic/user/trigger
mechanic/shopify/bulk_operation{% endcomment %}

{% assign customers = array %}

{% if event.topic contains "shopify/customers/" %}
  {% if event.preview %}
    {% assign customer = hash %}
    {% assign customer["admin_graphql_api_id"] = "gid://shopify/Customer/1234567890" %}
    {% assign customer["tags"] = "" %}
  {% endif %}

  {% assign customer_node = hash %}
  {% assign customer_node["id"] = customer.admin_graphql_api_id %}
  {% assign customer_node["tags"] = customer.tags | split: ", " %}
  {% assign customers[0] = customer_node %}
{% elsif event.topic == "mechanic/user/trigger" %}
  {% capture bulk_operation_query %}
    query {
      customers {
        edges {
          node {
            id
            firstName
            lastName
            tags
          }
        }
      }
    }
  {% endcapture %}

  {% action "shopify" %}
    mutation {
      bulkOperationRunQuery(
        query: {{ bulk_operation_query | json }}
      ) {
        bulkOperation {
          id
          status
        }
        userErrors {
          field
          message
        }
      }
    }
  {% endaction %}
{% elsif event.topic == "mechanic/shopify/bulk_operation" %}
  {% if event.preview %}
    {% capture objects_jsonl %}
      {"id":"gid://shopify/Customer/1234567890","tags": []}
    {% endcapture %}

    {% assign bulkOperation = hash %}
    {% assign bulkOperation["objects"] = objects_jsonl | parse_jsonl %}
  {% endif %}

  {% assign customers = bulkOperation.objects %}
{% endif %}

{% for customer in customers %}
  {%- comment -%}⚠⚠RUBBER MEETS THE ROAD{%- endcomment -%}
  {% assign delete_customer = false %}
  {% if customer.firstName == customer.lastName %}
    {% assign delete_customer = true %}
  {% endif %}

  {% assign add_tag = false %}
  {% assign remove_tag = false %}

  {% if customer.tags contains options.customer_tag_to_watch__required %}
    {% if customer.tags contains options.customer_tag_to_use_when_missing__required %}
      {% assign remove_tag = true %}
    {% endif %}
  {% else %}
    {% unless customer.tags contains options.customer_tag_to_use_when_missing__required %}
      {% assign add_tag = true %}
    {% endunless %}
  {% endif %}

  {% if add_tag or remove_tag %}
    {% action "shopify" %}
      mutation {
        tags{% if add_tag %}Add{% else %}Remove{% endif %}(
          id: {{ customer.id | json }}
          tags: {{ options.customer_tag_to_use_when_missing__required | json }}
        ) {
          userErrors {
            field
            message
          }
        }
      }
    {% endaction %}
  {% endif %}

  {% if options.ignore_customers_having_this_tag != blank %}
    {% assign customer_tags = customer.tags | split: ", " %}
    {% if customer_tags contains options.ignore_customers_having_this_tag %}
      {% log "Customer had a whitelisted tag; ignoring" %}
      {% assign delete_customer = false %}
    {% endif %}
  {% endif %}

  {%- comment -%}⚠ This is using the REST api?? So may need updating{%- endcomment -%}
  {% if event.preview or delete_customer %}
    {% action "shopify" %}
      [
        "delete",
        ["customer", {{ customer.id | json }}]
      ]
    {% endaction %}
  {% endif %}
{% endfor %}

i’ve got a custom gif of that for this stuff somewhere but can never find the darn thing!!

Hey,you can use Customers segment to filter the customers who haven’t purchanse , and delete them manually. pls refer to the below

:

Right, but Customer segments are not that flexible, though they do have info not available elsewhere.
Say, you can’t check firstName == lastName in segments.

However, one can start a Flow on “Customer joined a segment” trigger.

The screenshot is misleading; if you choose the “Delete” option highlighted in the screenshot, Shopify will delete the segment but not delete any of the customer profiles found by the segment.

The correct steps to bulk-deleting spam accounts, as of Apr 2026, are:

  1. Create a segment. Unfortunately it appears Shopify requires customers to use their AI tool to create the segment, so type something like “customers with 0 purchases and 0 check-outs who signed up more than 30 days ago”. Save the segment; you’ll have to give it a name.
  2. Spot-check the results to verify that the segment finds only the spam accounts you wish to delete.
  3. Click the checkbox at the top left corner of the results list, next to the “Customer name” column heading.
  4. Click the nearby text link that says “select 50+ customers in this segment”; this should check the checkboxes for every customer in the segment (not just the ~50 showing on the first page).
  5. At the top right corner of the results, next to the “Bulk edit” button, click the 3-dot menu, which expands to reveal a delete option.

The deletion could take several minutes to run, depending on the quantity being deleted. Shopify gives no progress bar, no indication that anything is happening. I assume the deletion would continue if you navigate away from the page, but that’s an assumption.

My site had accumulated thousands of spam accounts, including hundreds of “John Doe” accounts; deleting ~6000 took about 10 minutes.